@craco/craco, customize-cra, and react-app-rewired are utilities designed to override the locked Webpack and Babel configurations in Create React App (CRA) without triggering the irreversible eject command. react-app-rewired was the pioneer in this space, allowing config overrides via a script file. customize-cra is a companion library that provides functional helpers to make react-app-rewired overrides cleaner and safer. @craco/craco (Create React App Configuration Override) is a more modern alternative that offers a plugin ecosystem and a structured config file. Note that since Create React App is now archived and in maintenance mode, these tools are best suited for maintaining existing CRA applications rather than starting new greenfield projects.
When Create React App (CRA) was the standard for bootstrapping React projects, developers quickly hit its limitations. The default Webpack configuration was hidden, and the only official way to change it was to eject β a one-way operation that copies all build config into your repo, making future CRA updates painful. To solve this, the community built tools to override the config without ejecting. Let's compare the three main players: @craco/craco, customize-cra, and react-app-rewired.
The core difference lies in how they intercept the build process.
react-app-rewired replaces the react-scripts CLI commands.
package.json scripts to use react-app-rewired instead of react-scripts.config-overrides.js file in your root directory.// react-app-rewired: config-overrides.js
module.exports = function override(config, env) {
// Directly mutate or return new config object
config.resolve.alias['@'] = path.resolve(__dirname, 'src');
return config;
};
customize-cra is not a standalone runner.
react-app-rewired.react-app-rewired in your package.json scripts.// customize-cra: Used inside config-overrides.js
const { override, addBabelPlugin } = require('customize-cra');
module.exports = override(
addBabelPlugin('lodash'),
(config) => {
// Additional custom logic
return config;
}
);
@craco/craco replaces the scripts but uses a structured config file.
craco (e.g., craco start).craco.config.js file with a specific schema (webpack, babel, jest).// @craco/craco: craco.config.js
module.exports = {
webpack: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
babel: {
plugins: ['lodash'],
},
};
Extending the build tool often requires more than just one-off config changes.
react-app-rewired has no official plugin system.
config-overrides.js code.// react-app-rewired: Manual merging required
const { merge } = require('webpack-merge');
module.exports = function override(config) {
return merge(config, { /* custom config */ });
};
customize-cra offers functional plugins.
addWebpackAlias, addBabelPreset, etc.react-app-rewired runtime.// customize-cra: Functional helpers
const { addWebpackAlias } = require('customize-cra');
module.exports = override(
addWebpackAlias({ '@': path.resolve(__dirname, 'src') })
);
@craco/craco has a dedicated plugin API.
craco-alias, craco-less).// @craco/craco: Plugin usage
module.exports = {
plugins: [
{
plugin: require('craco-alias'),
options: {
source: 'tsconfig',
baseUrl: '.',
aliases: { '@': './src' },
},
},
],
};
This is the most critical factor for architectural decisions today.
react-app-rewired is in maintenance mode.
config-overrides.js is loaded.customize-cra follows react-app-rewired status.
rewired, it inherits the same risks.@craco/craco is the most actively maintained.
You want to import files using @/components instead of ../../components.
@craco/cracocraco-alias). No manual Webpack merging.// craco.config.js
module.exports = {
webpack: {
alias: { '@': path.resolve(__dirname, 'src') },
},
};
You need to enable legacy decorators for a legacy codebase.
customize-cra (if already on rewired) or @craco/craco.// customize-cra
module.exports = override(
addBabelPlugin(['@babel/plugin-proposal-decorators', { legacy: true }])
);
You plan to move to Vite or Next.js in 6 months but need fixes now.
@craco/cracoconfig-overrides.js. Easier to extract later.// craco.config.js
// This structure is easier to port to vite.config.js later
module.exports = {
webpack: { configure: (config) => { /* ... */ } },
};
| Feature | @craco/craco | customize-cra | react-app-rewired |
|---|---|---|---|
| Standalone | β Yes | β Needs rewired | β Yes |
| Config File | craco.config.js | config-overrides.js | config-overrides.js |
| Plugin System | β Yes | β οΈ Functional Helpers | β No |
| Maintenance | π’ Active | π‘ Low | π΄ Minimal |
| Security | π’ Better Isolation | π‘ Inherits Rewired | π΄ Known Risks |
| CRA Compatibility | High | High | High |
Important Context: Create React App is officially archived. Meta is no longer recommending it for new projects.
@craco/craco is the pragmatic choice for teams stuck on CRA.
react-app-rewired and customize-cra are legacy tools.
Final Thought: Do not start new projects with these tools.
@craco/craco to manage config cleanly until you can migrate.Choose @craco/craco for any new configuration work on existing CRA apps. It has an active plugin ecosystem, better TypeScript support, and does not require changing the package.json scripts as aggressively as rewired. It is the safest long-term bet for extending CRA before migrating to a different build tool.
Choose customize-cra only if you are already committed to react-app-rewired and want to clean up your override logic. It is not a standalone solution; it relies on react-app-rewired to function. It helps avoid manual Webpack config manipulation by providing tested helper functions.
Avoid react-app-rewired for new projects due to maintenance concerns and past security vulnerabilities related to how it loads config files. Only use it if you are maintaining a legacy codebase that already depends on it and migration to CRACO is too risky or costly.
Create React App Configuration Override, an easy and comprehensible configuration layer for create-react-app.
Find config docs, API docs, plugins, and example configs at craco.js.org!
Get all the benefits of Create React App and customization without using 'eject' by adding a single configuration (e.g. craco.config.js) file at the root of your application and customize your ESLint, Babel, PostCSS configurations and many more.
Install the latest version of the package from npm as a dev dependency:
npm i -D @craco/craco
Create a CRACO configuration file in your project's root directory and configure:
my-app
βββ node_modules
+ βββ craco.config.js
βββ package.json
Update the existing calls to react-scripts in the scripts section of your package.json to use the craco CLI:
"scripts": {
- "start": "react-scripts start"
+ "start": "craco start"
- "build": "react-scripts build"
+ "build": "craco build"
- "test": "react-scripts test"
+ "test": "craco test"
}
Visit craco.js.org to learn more.