# How to Add Custom Webpack Configuration to Create React App Without Ejecting

> Learn how to add custom Webpack configuration to Create React App without ejecting using tools like react-app-rewired or craco. Keep your CRA managed environment intact.

- Repository: [Meta/create-react-app](https://github.com/facebook/create-react-app)
- Tags: how-to-guide
- Published: 2026-02-26

---

**You cannot modify the internal [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js) directly because it is protected by `@remove-on-eject` markers, so you must use a wrapper tool like `react-app-rewired` or `craco` to programmatically extend the webpack configuration while keeping the benefits of a managed environment.**

Create React App (CRA) intentionally shields its build pipeline to provide a stable, zero-configuration environment. However, when you need to modify the underlying **webpack configuration**—such as adding aliases, custom loaders, or analysis plugins—you must work around the internal [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js) file that is locked behind `@remove-on-eject` markers. Fortunately, you can inject **custom webpack configuration into Create React App without ejecting** by using community-maintained wrapper tools that programmatically extend the default setup.

## Why the Internal Webpack Config Is Protected

The canonical webpack configuration lives in [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js) within the `react-scripts` package. This file contains the `@remove-on-eject` annotation pattern, which signals that the file is ephemeral and will be replaced during `react-scripts` upgrades. According to the facebook/create-react-app source code, any manual edits to this file would be overwritten on the next dependency update, making direct modification unsustainable.

The [`packages/react-scripts/config/paths.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/paths.js) module defines critical filesystem references like `paths.appSrc` and `paths.appBuild` that the webpack configuration consumes. While you can inspect these files to understand the default behavior, the architecture deliberately prevents runtime extension points—unlike [`packages/babel-preset-react-app/webpack-overrides.js`](https://github.com/facebook/create-react-app/blob/main/packages/babel-preset-react-app/webpack-overrides.js), which exposes a minimal macro detection override but **does not provide** a user-level API for general webpack customization.

## Method 1: Override with react-app-rewired and customize-cra

The `react-app-rewired` package replaces the `react-scripts` binary with a thin wrapper that loads a user-provided [`config-overrides.js`](https://github.com/facebook/create-react-app/blob/main/config-overrides.js). The wrapper calls the original CRA config function, lets you mutate the returned configuration object, and then hands it back to webpack.

### Installation and Setup

Install the wrapper and helper utilities as development dependencies:

```bash
npm install -D react-app-rewired customize-cra

```

Update your **[`package.json`](https://github.com/facebook/create-react-app/blob/main/package.json)** scripts to use the rewired binary instead of the default `react-scripts`:

```json
{
  "scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test"
  }
}

```

### Adding Plugins and Aliases

Create a **[`config-overrides.js`](https://github.com/facebook/create-react-app/blob/main/config-overrides.js)** file at the project root. This file exports a function that receives the default webpack configuration and returns the modified object:

```javascript
// config-overrides.js
const { override, addWebpackPlugin, addWebpackAlias } = require('customize-cra');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const path = require('path');

module.exports = override(
  // Add a new plugin
  addWebpackPlugin(
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      openAnalyzer: false,
      reportFilename: 'bundle-report.html',
    })
  ),

  // Add a custom alias to resolve "~components" to src/components
  addWebpackAlias({
    '~components': path.resolve(__dirname, 'src/components'),
  })
);

```

Running `npm run build` now generates a [`bundle-report.html`](https://github.com/facebook/create-react-app/blob/main/bundle-report.html) file and enables imports from `~components/...` without ejecting.

## Method 2: Use CRACO for Structured Configuration

**CRACO** (Create React App Configuration Override) provides a richer API than `react-app-rewired` and integrates with TypeScript, ESLint, and other CRA subsystems out-of-the-box. It uses a [`craco.config.js`](https://github.com/facebook/create-react-app/blob/main/craco.config.js) file that merges with CRA’s internal configuration via a structured API rather than imperative mutation.

### Installation and Configuration

Install the package:

```bash
npm install -D @craco/craco

```

Replace the default scripts in **[`package.json`](https://github.com/facebook/create-react-app/blob/main/package.json)**:

```json
{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  }
}

```

### Modifying Loaders and Rules

Create a **[`craco.config.js`](https://github.com/facebook/create-react-app/blob/main/craco.config.js)** file to add aliases, plugins, or modify existing loader rules:

```javascript
// craco.config.js
const path = require('path');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  webpack: {
    alias: {
      '@utils': path.resolve(__dirname, 'src/utils/'),
    },
    plugins: {
      add: [
        new BundleAnalyzerPlugin({
          analyzerMode: process.env.NODE_ENV === 'production' ? 'static' : 'disabled',
        }),
      ],
    },
    // Modify existing rule to support .svg as React components
    configure: (webpackConfig) => {
      const fileLoaderRule = webpackConfig.module.rules.find(
        (rule) => rule.oneOf && rule.oneOf.find((r) => r.loader && r.loader.includes('file-loader'))
      );
      if (fileLoaderRule) {
        const svgRule = {
          test: /\.svg$/,
          use: ['@svgr/webpack', 'file-loader'],
        };
        // Insert before the file-loader rule to intercept SVGs
        fileLoaderRule.oneOf.unshift(svgRule);
      }
      return webpackConfig;
    },
  },
};

```

This configuration enables importing SVGs as React components (`import { ReactComponent as Logo } from './logo.svg'`) and uses `@utils` as a path alias without touching the internal [`webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/webpack.config.js).

## Method 3: Fork react-scripts for Deep Customization

For scenarios requiring changes that wrapper tools cannot express—such as replacing the entire module resolution pipeline or modifying the [`paths.js`](https://github.com/facebook/create-react-app/blob/main/paths.js) logic—you can maintain a **custom `react-scripts` fork**. Clone the `react-scripts` package, apply your changes to its internal [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js), and reference the fork in your project’s [`package.json`](https://github.com/facebook/create-react-app/blob/main/package.json):

```json
{
  "react-scripts": "github:yourusername/react-scripts#your-branch"
}

```

This approach provides complete control but requires manual synchronization with upstream CRA updates to receive security patches and new features.

## Summary

- **[`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js)** is protected by `@remove-on-eject` markers and must never be edited directly in `node_modules`.
- **`react-app-rewired`** with **`customize-cra`** allows imperative mutation of the webpack config via [`config-overrides.js`](https://github.com/facebook/create-react-app/blob/main/config-overrides.js) for simple plugin and alias additions.
- **`@craco/craco`** offers a declarative API via [`craco.config.js`](https://github.com/facebook/create-react-app/blob/main/craco.config.js) for complex environment-specific overrides and TypeScript integration.
- A **custom `react-scripts` fork** is the only path for deep architectural changes that wrappers cannot accommodate.
- All methods allow you to run standard commands (`npm start`, `npm run build`, `npm test`) while transparently injecting your custom webpack settings.

## Frequently Asked Questions

### Can I directly edit the webpack.config.js file in node_modules without ejecting?

No. The file located at [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js) contains `@remove-on-eject` markers and is regenerated on every `react-scripts` upgrade. Any manual changes would be overwritten, breaking your build pipeline and preventing clean updates.

### What is the difference between react-app-rewired and CRACO?

**`react-app-rewired`** provides a minimal wrapper that calls a user-supplied function to mutate the webpack config object directly. **`CRACO`** offers a structured configuration API with dedicated sections for webpack, babel, eslint, and devServer, making it better suited for complex, multi-faceted customizations that need to coordinate across different build tools.

### Will using a wrapper tool prevent me from updating CRA?

No. Both `react-app-rewired` and `CRACO` act as transparent proxies to the underlying `react-scripts` commands. They intercept the configuration at runtime but do not modify the `react-scripts` package itself, allowing you to upgrade CRA versions normally. However, you should verify wrapper compatibility with major CRA releases in the wrapper's changelog.

### How do I add a simple path alias without ejecting?

Use either the `addWebpackAlias` helper from `customize-cra` in a [`config-overrides.js`](https://github.com/facebook/create-react-app/blob/main/config-overrides.js) file, or define the `webpack.alias` property in a [`craco.config.js`](https://github.com/facebook/create-react-app/blob/main/craco.config.js) file. Both methods inject the alias into the resolve configuration of the internal webpack setup without requiring ejection.