react-scripts is the official build and development toolchain for Create React App (CRA), providing a zero-configuration setup for React applications. react-app-rewired and customize-cra are complementary tools designed to override or extend CRA's webpack and Babel configurations without running npm eject. react-app-rewired acts as a drop-in replacement for react-scripts that enables custom configuration via a config-overrides.js file, while customize-cra provides a set of utility functions specifically for use within that overrides file to simplify common customization patterns like adding plugins, modifying loaders, or adjusting Webpack rules.
Create React App (CRA) abstracts away complex build tooling behind react-scripts, offering a streamlined developer experience. But what happens when you need to tweak Webpack, add a Babel plugin, or adjust PostCSS settings? That’s where react-app-rewired and customize-cra come in — though they solve the problem differently, and not all paths are equally sustainable. Let’s break down how these tools work, their trade-offs, and when to use each.
react-scripts is the official engine behind CRA. It bundles Webpack, Babel, ESLint, Jest, and other tools into a single dependency. You never configure these directly — everything is hidden behind abstraction.
// package.json (standard CRA)
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build"
}
react-app-rewired replaces react-scripts in your npm scripts and injects a custom configuration file (config-overrides.js) before CRA’s internal config runs. It patches the Webpack and Jest configs at runtime without ejecting.
// package.json (with rewired)
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build"
}
customize-cra is not a standalone tool. It’s a library of helper functions meant to be used inside config-overrides.js when you’re already using react-app-rewired. It simplifies common overrides with readable APIs.
// config-overrides.js
const { override, addWebpackAlias } = require('customize-cra');
module.exports = override(
addWebpackAlias({ '@': path.resolve(__dirname, 'src') })
);
⚠️ Critical Note: As of 2024, both
react-app-rewiredandcustomize-craare effectively unmaintained. Thereact-app-rewiredGitHub repository states: “This project is in maintenance mode... it may break with future versions of react-scripts.” Similarly,customize-crahasn’t seen meaningful updates since 2021. Do not use them in new projects.
Let’s compare how each approach handles a typical request: aliasing src to @.
react-scripts (No Customization)You can’t do this. CRA doesn’t expose Webpack config. Your only options are:
../../../components/Button)// No solution without ejecting or third-party tools
import Button from '../../../components/Button'; // verbose
react-app-rewired (Manual Override)You write raw Webpack manipulation code in config-overrides.js.
// config-overrides.js
const path = require('path');
module.exports = function override(config) {
config.resolve.alias = {
...config.resolve.alias,
'@': path.resolve(__dirname, 'src')
};
return config;
};
Now you can import cleanly:
import Button from '@/components/Button';
customize-cra (Declarative Helper)You use a helper function to avoid touching Webpack internals directly.
// config-overrides.js
const { override, addWebpackAlias } = require('customize-cra');
const path = require('path');
module.exports = override(
addWebpackAlias({
'@': path.resolve(__dirname, 'src')
})
);
Same result, but less error-prone if you’re unfamiliar with Webpack’s structure.
Suppose you want to use babel-plugin-styled-components for better debugging.
react-scriptsNot possible without ejecting.
react-app-rewiredYou must manually merge into the Babel config:
// config-overrides.js
module.exports = function override(config, env) {
// Find the Babel loader rule
const babelLoader = config.module.rules.find(
rule => rule.loader && rule.loader.includes('babel-loader')
);
if (babelLoader) {
babelLoader.options.plugins = [
...(babelLoader.options.plugins || []),
'babel-plugin-styled-components'
];
}
return config;
};
customize-craUse the built-in addBabelPlugin helper:
// config-overrides.js
const { override, addBabelPlugin } = require('customize-cra');
module.exports = override(
addBabelPlugin('babel-plugin-styled-components')
);
Again, customize-cra reduces boilerplate — but only if the helper exists for your use case.
All customization via react-app-rewired or customize-cra depends on CRA’s internal config structure remaining stable. When CRA updates react-scripts, your overrides can silently break because:
For example, CRA v5 introduced significant Webpack 5 changes. Many react-app-rewired setups broke because overrides assumed Webpack 4 patterns.
There’s no compile-time check — your app might build successfully but behave incorrectly at runtime (e.g., missing CSS, broken HMR).
If you’re maintaining a legacy CRA app that already uses react-app-rewired, proceed cautiously:
react-scripts to a known-working version in package.jsonFor new projects, do not start with react-app-rewired. Instead:
| Package | Standalone? | Purpose | Safe for New Projects? | Config Style |
|---|---|---|---|---|
react-scripts | ✅ Yes | Official CRA build tool | ✅ Yes | Zero-config |
react-app-rewired | ✅ Yes | Enables custom Webpack/Babel in CRA | ❌ No | Manual JS overrides |
customize-cra | ❌ No | Helper utilities for react-app-rewired | ❌ No | Declarative helpers |
react-scripts if you can live within CRA’s boundaries. It’s stable, secure, and officially supported.react-app-rewired and customize-cra in new codebases. Their maintenance status and fragility make them technical debt traps.The original promise of CRA was “no configuration needed.” If you’ve outgrown that, it’s time to graduate to a tool that embraces configurability rather than fighting against it.
Choose react-scripts if you prefer a stable, officially supported, zero-config experience and don’t need to modify the underlying Webpack, Babel, or ESLint configurations. It’s the default and recommended choice for most CRA projects. If your customization needs grow beyond what react-app-rewired can safely support, consider migrating to a framework like Vite or Next.js instead of forcing unsupported overrides.
Choose customize-cra if you're already using react-app-rewired and want a set of well-tested helper functions to modify CRA’s Webpack config in a more declarative way. It’s ideal for common tasks like adding PostCSS plugins, aliasing modules, or injecting environment variables without writing raw Webpack manipulation code. However, it only works alongside react-app-rewired and doesn’t function independently.
Choose react-app-rewired when you need to customize CRA’s build configuration without ejecting, especially if you require fine-grained control over Webpack or Babel settings. It replaces react-scripts in your npm scripts and loads overrides from config-overrides.js. Be aware that it may break with new CRA releases since it relies on internal implementation details, and it’s best suited for projects where ejecting is not an option but minor tweaks are necessary.
This package includes scripts and configuration used by Create React App.
Please refer to its documentation: