# How to Customize the Babel Configuration in Create React App Without Ejecting

> Customize Babel in Create React App without ejecting. Learn to use CRACO or react-app-rewired for flexible Babel configuration and macros.

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

---

**Create React App deliberately disables external Babel configuration files via `babelrc: false` and `configFile: false` in its Webpack setup, forcing developers to use Babel macros or configuration override tools like CRACO and react-app-rewired to customize Babel without ejecting.**

Create React App (CRA) provides a zero-configuration build setup that intentionally shields users from underlying tooling complexity. However, when you need to add custom Babel plugins or presets to the `facebook/create-react-app` toolchain, you'll find that standard configuration files like `.babelrc` or [`babel.config.js`](https://github.com/facebook/create-react-app/blob/main/babel.config.js) are ignored by design.

## Why CRA Blocks Direct Babel Configuration

In [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js) (lines 433-435), the `babel-loader` options explicitly disable external configuration:

```javascript
babelrc: false,
configFile: false,

```

These settings tell Babel to ignore any `.babelrc`, [`babel.config.js`](https://github.com/facebook/create-react-app/blob/main/babel.config.js), or `babel` field in [`package.json`](https://github.com/facebook/create-react-app/blob/main/package.json). This design ensures build stability across CRA releases but prevents direct customization of the Babel pipeline.

## Method 1: Use Babel Macros

CRA ships with `babel-plugin-macros` pre-installed, providing a way to run compile-time code transformations without modifying the core Babel configuration. Macros are configured via [`macro.config.json`](https://github.com/facebook/create-react-app/blob/main/macro.config.json) in your project root.

Create a [`macro.config.json`](https://github.com/facebook/create-react-app/blob/main/macro.config.json) file:

```json
{
  "my-macro": {
    "optionA": true
  }
}

```

Then use the macro in your source code:

```javascript
// src/example.js
import myMacro from 'my-macro.macro';

const result = myMacro('input');

```

## Method 2: Override Configuration with CRACO

**CRACO** (Create React App Configuration Override) is the most robust solution for modifying Babel settings without ejecting. It intercepts CRA's configuration before Webpack runs, allowing you to inject custom plugins and presets.

First, install CRACO as a development dependency:

```bash
npm install @craco/craco --save-dev

```

Create a [`craco.config.js`](https://github.com/facebook/create-react-app/blob/main/craco.config.js) file in your project root to extend Babel:

```javascript
module.exports = {
  babel: {
    plugins: [
      ['@babel/plugin-proposal-optional-chaining', { loose: false }],
      ['@babel/plugin-proposal-decorators', { legacy: true }],
    ],
    presets: [
      ['@babel/preset-env', { targets: { node: 'current' } }],
    ],
  },
};

```

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

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

```

## Method 3: Use react-app-rewired with customize-cra

As an alternative to CRACO, you can use **react-app-rewired** paired with **customize-cra**. This combination provides a function-based API for overriding CRA's internal Webpack and Babel configurations.

Install both packages:

```bash
npm install customize-cra react-app-rewired --save-dev

```

Create a [`config-overrides.js`](https://github.com/facebook/create-react-app/blob/main/config-overrides.js) file:

```javascript
const { override, addBabelPlugin } = require('customize-cra');

module.exports = override(
  addBabelPlugin(['@babel/plugin-proposal-nullish-coalescing-operator']),
  addBabelPlugin(['@babel/plugin-proposal-do-expressions'])
);

```

Update your [`package.json`](https://github.com/facebook/create-react-app/blob/main/package.json) scripts:

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

```

## Method 4: Ejecting (Last Resort)

If you require full control over the Babel pipeline—such as modifying the core preset order or replacing `babel-preset-react-app`—you must run `npm run eject`. This command exposes all configuration files, including [`webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/webpack.config.js), allowing you to remove the `babelrc: false` and `configFile: false` restrictions.

**Warning:** Ejecting is a one-way operation. You cannot revert to the protected CRA setup, and you lose the seamless upgrade path for `react-scripts`.

## Summary

- CRA explicitly disables external Babel configuration via `babelrc: false` and `configFile: false` in [`packages/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/packages/react-scripts/config/webpack.config.js) to ensure build stability.
- **Babel macros** provide compile-time transformations without configuration files, utilizing the pre-installed `babel-plugin-macros`.
- **CRACO** and **react-app-rewired** are the officially recommended paths to customize Babel plugins and presets without ejecting.
- **Ejecting** remains the only option for fundamental changes to the Babel pipeline, but it permanently exits the managed CRA environment.

## Frequently Asked Questions

### How can I tell if my Babel configuration is being loaded by CRA?

If your custom plugins are not transforming code as expected, CRA is likely ignoring your configuration. Verify this by inspecting [`node_modules/react-scripts/config/webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/node_modules/react-scripts/config/webpack.config.js) for the `babelrc: false` and `configFile: false` settings, which explicitly disable external Babel configuration files.

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

Both tools allow you to customize CRA without ejecting, but CRACO provides a more structured plugin API and dedicated configuration file ([`craco.config.js`](https://github.com/facebook/create-react-app/blob/main/craco.config.js)) specifically designed for Babel and Webpack overrides. react-app-rewired uses a function-based override system via [`config-overrides.js`](https://github.com/facebook/create-react-app/blob/main/config-overrides.js) and is typically paired with `customize-cra` for convenience methods.

### Can I use TypeScript with custom Babel plugins in CRA?

Yes. CRA supports TypeScript out of the box, and configuration override tools like CRACO can inject Babel plugins that process TypeScript files. Ensure your custom Babel plugins are compatible with TypeScript syntax or are configured to run after the TypeScript preset transformation to avoid compilation errors.

### Is ejecting from CRA reversible?

No. Once you run `npm run eject`, the command exposes all configuration files—including [`webpack.config.js`](https://github.com/facebook/create-react-app/blob/main/webpack.config.js) and Babel configurations—directly into your project directory. This is a permanent operation that cannot be undone, and you lose the ability to receive seamless updates to `react-scripts`. Always exhaust macro and override tool options before ejecting.