Create React App (CRA) is a Lerna monorepo providing a zero-configuration toolchain for bootstrapping and developing React applications. This guide covers the full internal architecture, every configuration surface, the build pipeline, testing infrastructure, and operational concerns for administrators, contributors, and developers who maintain or extend CRA-based projects.
For installation walkthroughs, everyday usage, and feature tutorials, see the User Guide.
Important
Create React App is officially deprecated as of the latest source. The CLI
init()function displays a deprecation warning, and the Docusaurus website metadata states "Create React App is deprecated." This guide remains valuable for the thousands of production applications still running on CRA, and for understanding the architectural decisions that influenced the React ecosystem.
1. Architecture Overview
1.1 Monorepo Structure
CRA uses Lerna with npm workspaces. The top-level lerna.json declares the package locations, and package.json configures workspaces:
create-react-app/
├── packages/
│ ├── create-react-app/ # CLI scaffolder (5.1.0)
│ ├── react-scripts/ # Build toolchain (5.1.0)
│ ├── react-dev-utils/ # 33 shared utility modules (12.1.0)
│ ├── react-error-overlay/ # Browser error overlay (6.1.0)
│ ├── babel-preset-react-app/ # Babel preset (10.1.0)
│ ├── babel-plugin-named-asset-import/ # SVG named imports (0.4.0)
│ ├── eslint-config-react-app/ # ESLint configuration (7.1.0)
│ ├── confusing-browser-globals/ # Restricted globals list (1.0.11)
│ ├── react-app-polyfill/ # IE9/IE11/jsdom/stable polyfills (3.0.0)
│ ├── cra-template/ # Default JS template (1.3.0)
│ └── cra-template-typescript/ # TypeScript template (1.3.0)
├── docusaurus/
│ └── website/ # Documentation site
└── test/ # E2E integration tests
1.2 Package Dependency Graph
The packages form a layered dependency tree:
create-react-app (CLI)
└── spawns npx react-scripts init
react-scripts (build toolchain)
├── babel-preset-react-app
│ └── babel-plugin-named-asset-import
├── eslint-config-react-app
│ └── confusing-browser-globals
├── react-dev-utils (33 modules)
│ └── react-error-overlay
└── react-app-polyfill (via templates)
cra-template / cra-template-typescript
└── consumed by create-react-app during scaffolding
1.3 Design Principles
- Zero configuration —
react-scriptsencapsulates all tooling config so end users never touch webpack, Babel, or ESLint directly. - Single dependency — A generated project depends only on
react-scripts(plus React itself). All build tools are transitive dependencies. - Eject escape hatch — Running
react-scripts ejectcopies all configuration and scripts into the project, removing thereact-scriptsdependency permanently. - Convention over configuration — Fixed directory structure (
src/,public/,build/), fixed entry points (src/index.js,public/index.html), environment variable prefix (REACT_APP_*).
Note
The "single dependency" model means that upgrading
react-scriptsupgrades webpack, Babel, ESLint, Jest, PostCSS, and every other tool simultaneously. This is both the primary strength (simplicity) and primary pain point (version lock-in) of the architecture.
2. System Requirements
2.1 Runtime Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| Node.js | 14.0.0 | 18.x LTS or 20.x LTS |
| npm | 6.x | 8.x+ |
| Yarn (optional) | 1.x | 1.22+ |
| pnpm (optional) | 6.x | 8.x+ |
| Disk space | 200 MB (node_modules) | 500 MB+ (with cache) |
| RAM | 1 GB free | 4 GB+ (large projects) |
2.2 Node.js Version Constraints
The CLI (create-react-app/index.js) enforces a minimum Node version check at startup. The engines field in react-scripts/package.json specifies the supported range.
{
"engines": {
"node": ">=14"
}
}
Warning
Node.js 17+ changed the default OpenSSL provider, causing
ERR_OSSL_EVP_UNSUPPORTEDerrors with webpack's hash functions. The workaround is settingNODE_OPTIONS=--openssl-legacy-providerbefore running build commands. This is a frequently reported issue (GitHub #17216).
2.3 Browser Support
Default browserslist configuration in generated projects:
{
"browserslist": {
"production": [">0.2%", "not dead", "not op_mini all"],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
The react-app-polyfill package provides targeted polyfills:
| Import | Target |
|---|---|
react-app-polyfill/ie9 |
IE9+ (includes IE11 polyfills) |
react-app-polyfill/ie11 |
IE11+ |
react-app-polyfill/stable |
All stable JS features |
react-app-polyfill/jsdom |
jsdom test environment |
3. Configuration Reference
3.1 Environment Variables — Complete Reference
CRA uses environment variables as its primary configuration mechanism. Variables are loaded from .env files by config/env.js using dotenv and dotenv-expand.
.env File Loading Order
Files are loaded in this exact precedence (highest priority first):
.env.{NODE_ENV}.local # e.g., .env.development.local
.env.local # NOT loaded in test environment
.env.{NODE_ENV} # e.g., .env.development
.env # Base defaults
All REACT_APP_* prefixed variables are injected into the client bundle via webpack's DefinePlugin as process.env.REACT_APP_*.
Build System Variables
| Variable | Default | Used In | Description |
|---|---|---|---|
NODE_ENV |
development |
env.js, webpack | Build mode: development, production, or test |
BABEL_ENV |
same as NODE_ENV | env.js | Babel environment (set to match NODE_ENV) |
PUBLIC_URL |
/ |
paths.js, webpack | Base URL for the application (maps to homepage in package.json) |
BUILD_PATH |
build |
paths.js | Output directory for production builds |
GENERATE_SOURCEMAP |
true |
webpack.config.js | Set to false to skip production source maps |
INLINE_RUNTIME_CHUNK |
true |
webpack.config.js | Set to false to not inline the webpack runtime chunk |
IMAGE_INLINE_SIZE_LIMIT |
10000 |
webpack.config.js | Max bytes for inlining images as data URIs |
FAST_REFRESH |
true |
webpack.config.js | Enable React Fast Refresh in development |
TSC_COMPILE_ON_ERROR |
false |
webpack.config.js | Allow builds with TypeScript errors |
DISABLE_ESLINT_PLUGIN |
false |
webpack.config.js | Completely disable ESLint during builds |
ESLINT_NO_DEV_ERRORS |
false |
eslintFormatter.js | Downgrade ESLint errors to warnings in dev |
DISABLE_NEW_JSX_TRANSFORM |
false |
babel-preset-react-app | Use classic React.createElement instead of automatic JSX transform |
Development Server Variables
| Variable | Default | Used In | Description |
|---|---|---|---|
HOST |
0.0.0.0 |
start.js, webpackDevServer.config.js | Dev server bind address |
PORT |
3000 |
start.js | Dev server port (auto-increments if occupied) |
HTTPS |
false |
start.js | Enable HTTPS for dev server |
SSL_CRT_FILE |
none | getHttpsConfig.js | Path to custom SSL certificate file |
SSL_KEY_FILE |
none | getHttpsConfig.js | Path to custom SSL key file |
DANGEROUSLY_DISABLE_HOST_CHECK |
false |
webpackDevServer.config.js | Disable host header validation |
WDS_SOCKET_HOST |
window.location.hostname |
webpackHotDevClient.js | WebSocket host for HMR |
WDS_SOCKET_PATH |
/ws |
webpackHotDevClient.js | WebSocket path for HMR |
WDS_SOCKET_PORT |
window.location.port |
webpackHotDevClient.js | WebSocket port for HMR |
BROWSER |
system default | openBrowser.js | Browser to open (name, path, or none) |
BROWSER_ARGS |
none | openBrowser.js | Arguments passed to the browser process |
Editor Integration Variables
| Variable | Default | Used In | Description |
|---|---|---|---|
REACT_EDITOR |
falls through | launchEditor.js | Editor for click-to-source (highest priority) |
VISUAL |
falls through | launchEditor.js | Editor fallback (second priority) |
EDITOR |
falls through | launchEditor.js | Editor fallback (third priority) |
CI/Test Variables
| Variable | Default | Used In | Description |
|---|---|---|---|
CI |
false |
build.js, test.js | Treat warnings as errors in build; run tests non-interactively |
SKIP_PREFLIGHT_CHECK |
false |
react-scripts bin | Skip the babel/webpack/jest version conflict check |
Tip
When deploying to CI, set
CI=trueto make the build fail on any ESLint warning. This catches issues that would be silently ignored in local development. The test runner also changes behavior: it runs all tests once and exits instead of entering watch mode.
3.2 package.json Configuration
CRA reads several fields from the project's package.json:
| Field | Purpose |
|---|---|
homepage |
Sets PUBLIC_URL for asset paths (e.g., "https://myapp.com/subdir") |
proxy |
Single-origin API proxy for development (e.g., "http://localhost:4000") |
browserslist |
Target browsers for Babel and PostCSS Autoprefixer |
eslintConfig |
ESLint configuration (extends react-app by default) |
jest |
Jest overrides (limited — see Section 8.4) |
3.3 Advanced Proxy Configuration
For multi-target proxying, create src/setupProxy.js:
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function(app) {
app.use(
'/api',
createProxyMiddleware({
target: 'http://localhost:5000',
changeOrigin: true,
})
);
};
This file is loaded by the dev server automatically. It receives the Express app instance and can register any middleware.
3.4 TypeScript Configuration
When tsconfig.json is present, config/modules.js reads it to configure:
- baseUrl — Creates webpack
resolve.modulesentry and JestmoduleDirectories - paths — Maps to webpack
resolve.aliasentries and JestmoduleNameMapper
The TypeScript compiler itself runs via ForkTsCheckerWebpackPlugin in a separate process. Type errors are reported but do not block compilation unless TSC_COMPILE_ON_ERROR is false (the default).
{
"compilerOptions": {
"baseUrl": "src",
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
}
}
Note
CRA overwrites certain
tsconfig.jsonfields on startup. Fields likemodule,moduleResolution,isolatedModules, andjsxare forced to values compatible with the webpack pipeline. The rewrite is logged to the console.
3.5 Path Resolution
config/paths.js resolves all project paths from the app root. The resolved paths object:
| Key | Default Path | Description |
|---|---|---|
dotenv |
.env |
Base environment file |
appPath |
. |
Project root |
appBuild |
build (or BUILD_PATH) |
Production output |
appPublic |
public |
Static assets |
appHtml |
public/index.html |
HTML template |
appIndexJs |
src/index.{js,jsx,ts,tsx} |
Application entry point |
appPackageJson |
package.json |
Project manifest |
appSrc |
src |
Source directory |
appTsConfig |
tsconfig.json |
TypeScript config |
appJsConfig |
jsconfig.json |
JavaScript config |
yarnLockFile |
yarn.lock |
Yarn lockfile |
testsSetup |
src/setupTests.{js,ts} |
Jest setup file |
proxySetup |
src/setupProxy.js |
Dev proxy config |
appNodeModules |
node_modules |
Dependencies |
appWebpackCache |
node_modules/.cache |
Webpack persistent cache |
appTsBuildInfoFile |
node_modules/.cache/tsconfig.tsbuildinfo |
TS incremental build |
swSrc |
src/service-worker.{js,ts} |
Service worker source |
Module file extensions searched in order:
const moduleFileExtensions = [
'web.mjs', 'mjs', 'web.js', 'js',
'web.ts', 'ts', 'web.tsx', 'tsx',
'json', 'web.jsx', 'jsx',
];
4. Deployment & Scaling
4.1 Production Build Pipeline
Running react-scripts build (scripts/build.js) executes this sequence:
- Sets
NODE_ENV=productionandBABEL_ENV=production - Loads
.envfiles viaconfig/env.js - Verifies
public/index.htmlandsrc/index.jsexist (checkRequiredFiles) - Measures existing build sizes for diff reporting (
FileSizeReporter) - Cleans the
BUILD_PATHdirectory - Copies
public/to build output (excludingindex.html) - Runs webpack with production configuration
- Reports gzipped file sizes with before/after comparison
- Prints hosting instructions (
printHostingInstructions)
The production webpack configuration enables:
- Code splitting — Dynamic
import()creates separate chunks - Tree shaking — Unused exports eliminated via webpack's module concatenation
- Minification — TerserPlugin (JS, targeting ECMAScript 5 output) and CssMinimizerPlugin (CSS)
- Asset hashing — Content hashes in filenames for cache busting (
[name].[contenthash:8].js) - Chunk optimization —
splitChunks.chunks: 'all'withruntimeChunk: true - Source maps — Generated unless
GENERATE_SOURCEMAP=false
4.2 Build Output Structure
build/
├── static/
│ ├── js/
│ │ ├── main.[hash].js # Application code
│ │ ├── main.[hash].js.map # Source map
│ │ ├── [id].[hash].chunk.js # Split chunks
│ │ └── runtime-main.[hash].js # Webpack runtime (or inlined)
│ ├── css/
│ │ ├── main.[hash].css # Extracted CSS
│ │ └── main.[hash].css.map # CSS source map
│ └── media/
│ └── [name].[hash].[ext] # Images, fonts, other assets
├── index.html # Processed HTML with injected assets
├── asset-manifest.json # Map of logical names to hashed paths
├── manifest.json # PWA web app manifest (from public/)
└── service-worker.js # Generated by WorkboxWebpackPlugin (if src/service-worker.js exists)
4.3 Deployment Targets
The printHostingInstructions.js utility detects the deployment target from package.json and prints context-specific instructions.
Static File Server
npm install -g serve
serve -s build
The -s flag enables single-page app mode (rewrites all routes to index.html).
Subpath Deployment
Set homepage in package.json to configure the base URL:
{
"homepage": "https://example.com/my-app"
}
This sets PUBLIC_URL to /my-app, ensuring all asset paths are correctly prefixed. The %PUBLIC_URL% placeholder in public/index.html is replaced at build time by InterpolateHtmlPlugin.
CDN / Custom Build Directory
BUILD_PATH=dist react-scripts build
Tip
For GitHub Pages deployment, set
"homepage": "https://username.github.io/repo-name". For relative paths (useful when the deployment path is unknown at build time), set"homepage": "."— this makes all asset references relative.
4.4 Service Worker (PWA)
CRA supports Progressive Web App features via Workbox. When src/service-worker.js (or .ts) exists, WorkboxWebpackPlugin.InjectManifest processes it:
- Injects a precache manifest of all build assets
- The service worker controls caching strategy
- The default template from
cra-templateregisters the service worker only in production
In development, noopServiceWorkerMiddleware serves a no-op service worker that clears any previously registered cache, preventing stale content.
5. Operations
5.1 The Four Commands
react-scripts start (scripts/start.js)
- Sets
NODE_ENV=development - Verifies required files exist
- Detects available port (default 3000, auto-increments via
detect-port-alt) - Creates webpack compiler with development config
- Starts webpack-dev-server with HMR and error overlay
- Opens browser (unless
BROWSER=none)
Key dev server features:
- Hot Module Replacement via
webpackHotDevClient.js(not webpack's default client) - React Fast Refresh for state-preserving component updates (unless
FAST_REFRESH=false) - Error overlay (
react-error-overlay) for runtime and compilation errors - Proxy support (single-origin via
package.jsonor multi-target viasrc/setupProxy.js) - History API fallback for SPA routing
- Static file serving from
public/
react-scripts build (scripts/build.js)
Described in Section 4.1 above. Exits with code 1 on failure (or when CI=true and warnings exist).
react-scripts test (scripts/test.js)
- Sets
NODE_ENV=test - Loads
.envfiles (skips.env.local) - Configures Jest with
config/jest/transforms - Runs in watch mode by default (unless
CI=trueor--watchAll=false)
See Section 8.4 for full testing details.
react-scripts eject (scripts/eject.js)
Eject is a one-way operation that copies all encapsulated configuration into the project:
- Confirms via interactive prompt (unless
--force) - Copies
config/directory (webpack, Babel, Jest, env, paths, etc.) - Copies
scripts/directory (start, build, test) - Updates
package.json:- Moves all
react-scriptsdependencies to the project - Replaces script commands with direct
node scripts/*.jscalls - Removes
react-scriptsdependency - Adds
jest,babel, andeslintConfigsections
- Moves all
- Stages all changes in git (if repository detected)
Caution
Ejecting is irreversible. Once ejected, the project assumes ownership of all configuration files. Future
react-scriptsupdates (bug fixes, security patches, dependency upgrades) will not apply. Before ejecting, consider alternatives:CRACO,react-app-rewired, orcustomize-crafor non-destructive config overrides.
5.2 Preflight Checks
Before running any command, react-scripts performs a verifyPackageTree() check that detects mismatched versions of critical dependencies (Babel, webpack, Jest, ESLint). This catches scenarios where a parent project or global install provides a conflicting version.
Set SKIP_PREFLIGHT_CHECK=true to bypass this (not recommended for production).
5.3 Webpack Cache Management
CRA uses webpack 5's persistent filesystem cache stored in node_modules/.cache/:
cache: {
type: 'filesystem',
version: environmentHash, // Hash of NODE_ENV + all env vars
cacheDirectory: paths.appWebpackCache,
store: 'pack',
buildDependencies: {
defaultWebpack: ['webpack/lib/'],
config: [__filename],
tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f => fs.existsSync(f)),
},
}
The cache is automatically invalidated when:
- Any environment variable changes (via
environmentHash) - webpack config file changes
tsconfig.jsonorjsconfig.jsonchanges
To manually clear the cache:
rm -rf node_modules/.cache
5.4 Error Handling
The error overlay (react-error-overlay) injects an iframe into the page to display:
- Compilation errors (webpack build failures)
- Runtime errors (uncaught exceptions, unhandled promise rejections)
- Source-mapped stack traces with click-to-open-in-editor
The overlay hooks into window.onerror and window.onunhandledrejection. In production builds, the overlay is not included.
Warning
The dev server's
DANGEROUSLY_DISABLE_HOST_CHECKflag disables DNS rebinding protection. Only use this in trusted network environments. If you need to access the dev server from other devices, prefer--host 0.0.0.0with the default host check enabled.
6. Performance
6.1 Webpack Production Optimizations
| Optimization | Implementation | Impact |
|---|---|---|
| Code splitting | splitChunks.chunks: 'all' |
Separates vendor code from application code |
| Tree shaking | mode: 'production' + sideEffects |
Eliminates dead code exports |
| JS minification | TerserPlugin (parallel, ECMAScript 5 output) | 60-80% size reduction |
| CSS minification | CssMinimizerPlugin | Removes whitespace, deduplicates rules |
| Asset hashing | [contenthash:8] in filenames |
Enables aggressive caching |
| Runtime inlining | InlineChunkHtmlPlugin | Saves one HTTP request for runtime chunk |
| Image inlining | url-loader with 10KB limit | Reduces HTTP requests for small images |
| Scope hoisting | optimization.concatenateModules |
Reduces module wrapper overhead |
| Deterministic IDs | optimization.moduleIds: 'deterministic' |
Stable chunk hashes across builds |
6.2 Development Performance
| Feature | Implementation | Impact |
|---|---|---|
| Fast Refresh | ReactRefreshWebpackPlugin | Sub-second updates, preserves state |
| Filesystem cache | webpack 5 persistent cache | Faster rebuilds after first compilation |
cheap-module-source-map |
devtool setting | Faster source maps (line-only) |
| Ignored node_modules | watchOptions.ignored (if configured) | Reduced filesystem watching overhead |
| Fork TS Checker | ForkTsCheckerWebpackPlugin | Type checking in separate process |
6.3 Build Size Analysis
CRA includes FileSizeReporter which reports gzipped sizes after each build:
File sizes after gzip:
48.12 KB build/static/js/main.a1b2c3d4.js
1.42 KB build/static/css/main.e5f6a7b8.css
For deeper analysis, the asset-manifest.json maps logical names to physical files with hashes, enabling automated size tracking.
6.4 CSS Performance
- CSS Modules:
.module.cssand.module.scssfiles get locally scoped class names viacss-loadermodules mode, using the ident pattern[path][name]__[local]--[hash:base64:5](generated bygetCSSModuleLocalIdent) - PostCSS Autoprefixer: Automatically adds vendor prefixes based on browserslist
- Tailwind CSS: Auto-detected when
tailwind.config.jsexists in project root; PostCSS config includes tailwindcss plugin - MiniCssExtractPlugin: Production CSS is extracted to separate files (development uses
style-loaderfor HMR)
Note
Sass support requires installing
sass(Dart Sass). The webpack config conditionally enablessass-loaderfor.scssand.sassfiles, including CSS Module variants (.module.scss,.module.sass). No configuration is needed beyond the install.
7. Security
7.1 Environment Variable Isolation
Only variables prefixed with REACT_APP_ are embedded in the client bundle. This is enforced in config/env.js:
const REACT_APP = /^REACT_APP_/i;
function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key))
.reduce((env, key) => {
env[key] = process.env[key];
return env;
}, {
NODE_ENV: process.env.NODE_ENV || 'development',
PUBLIC_URL: publicUrl,
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
});
// ...
}
Caution
Any
REACT_APP_*variable is embedded directly into the JavaScript bundle and visible to anyone who inspects the built files. Never store secrets, API keys with write access, database credentials, or other sensitive values inREACT_APP_*variables. These are for public configuration only (e.g., API base URLs, feature flags, analytics IDs).
7.2 Module Scope Enforcement
ModuleScopePlugin restricts imports to files within src/ and node_modules/. This prevents:
- Accidentally importing build artifacts
- Importing files outside the source tree that webpack might not process correctly
- Breaking the build when files are in unexpected locations
Allowed exceptions: package.json (for reading homepage, proxy, etc.) and files in node_modules/.
7.3 Import Restriction — Confusing Browser Globals
confusing-browser-globals exports a list of 56 browser global variables (event, name, status, length, origin, open, close, etc.) that are restricted by the ESLint config. This prevents accidental use of global variables that shadow common local variable names:
// ESLint error: Unexpected use of 'event'
function handleClick() {
console.log(event); // Meant to use the parameter, accidentally got window.event
}
7.4 Host Check Protection
The webpack dev server validates the Host header to prevent DNS rebinding attacks. The webpackDevServer.config.js allows:
localhostand loopback addresses- The configured
HOSTenvironment variable - Any host when
DANGEROUSLY_DISABLE_HOST_CHECK=true(insecure)
7.5 HTTPS Configuration
For local HTTPS development:
HTTPS=true react-scripts start
For custom certificates (e.g., for testing with real domain names):
SSL_CRT_FILE=./cert.pem SSL_KEY_FILE=./key.pem HTTPS=true react-scripts start
The getHttpsConfig.js module reads and validates the certificate and key files, providing clear error messages if they are missing or unreadable.
7.6 Source Map Security
Production source maps are generated by default, exposing the original source code. For production deployments where source exposure is a concern:
GENERATE_SOURCEMAP=false react-scripts build
Alternatively, upload source maps to an error tracking service (Sentry, Bugsnag) and exclude .map files from the production server.
Important
The
asset-manifest.jsonfile lists all build outputs including source maps. If you serve this file publicly, automated tools can discover and download your source maps even if they are not linked in the JavaScript files.
8. Developer Internals
8.1 Webpack Configuration Deep Dive
config/webpack.config.js exports a function that takes webpackEnv ('development' or 'production') and returns the full webpack config. Key structural decisions:
Entry Points
entry: [
// Development: WebSocket client for HMR
isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'),
// Application entry
paths.appIndexJs,
].filter(Boolean)
Output Configuration
output: {
path: paths.appBuild,
pathinfo: isEnvDevelopment, // Module path comments in dev
filename: isEnvProduction
? 'static/js/[name].[contenthash:8].js'
: 'static/js/bundle.js',
chunkFilename: isEnvProduction
? 'static/js/[name].[contenthash:8].chunk.js'
: 'static/js/[name].chunk.js',
assetModuleFilename: 'static/media/[name].[hash][ext]',
publicPath: paths.publicUrlOrPath,
}
Loader Pipeline (in order of rule matching)
| Rule | Test Pattern | Loader(s) | Notes |
|---|---|---|---|
| Source maps | *.{js,mjs,jsx,ts,tsx,css} |
source-map-loader | Pre-loader; loads existing source maps from dependencies |
| Application JS | *.{js,mjs,jsx,ts,tsx} inside appSrc |
babel-loader | With babel-preset-react-app; plugins include React Refresh (dev) |
| Dependency JS | *.{js,mjs} outside appSrc |
babel-loader | Limited preset (no JSX, no TS); compiles modern JS in node_modules |
| CSS | *.css (not .module.css) |
style-loader/MiniCssExtractPlugin, css-loader, postcss-loader | PostCSS with Autoprefixer (+ Tailwind if detected) |
| CSS Modules | *.module.css |
Same as CSS + css-loader modules mode | Local scoped class names |
| Sass | *.{scss,sass} (not .module) |
Same as CSS + resolve-url-loader + sass-loader | Requires sass package installed |
| Sass Modules | *.module.{scss,sass} |
Same as Sass + css-loader modules mode | Local scoped class names |
| SVG (named import) | *.svg |
@svgr/webpack + file-loader | Named imports get React components; default gets URL |
| Catch-all assets | Everything else | asset/resource | Emits file, returns URL |
Complete Plugin List
| Plugin | Environment | Purpose |
|---|---|---|
| HtmlWebpackPlugin | Both | Generates index.html with injected script/link tags |
| InlineChunkHtmlPlugin | Production | Inlines the runtime chunk script into HTML |
| InterpolateHtmlPlugin | Both | Replaces %ENV_VARIABLE% placeholders in index.html |
| ModuleNotFoundPlugin | Both | Friendly error messages for missing modules with install suggestions |
| DefinePlugin | Both | Injects process.env.* as compile-time constants |
| CaseSensitivePathsPlugin | Development | Catches case-sensitivity issues on case-insensitive filesystems |
| ReactRefreshWebpackPlugin | Development (FAST_REFRESH) | Enables React Fast Refresh HMR |
| MiniCssExtractPlugin | Production | Extracts CSS into separate files |
| WebpackManifestPlugin | Both | Generates asset-manifest.json |
| WorkboxWebpackPlugin.InjectManifest | Production (if SW exists) | Service worker precache manifest injection |
| ForkTsCheckerWebpackPlugin | Both (if TS) | TypeScript type checking in separate process |
| ESLintPlugin | Both (unless DISABLE_ESLINT_PLUGIN) | Runs ESLint during compilation |
8.2 Babel Configuration
babel-preset-react-app configures Babel with these presets and plugins:
Presets
| Preset | Config | Notes |
|---|---|---|
@babel/preset-env |
useBuiltIns: 'entry', corejs: 3 |
Transpiles to target browsers |
@babel/preset-react |
runtime: 'automatic' (unless DISABLE_NEW_JSX_TRANSFORM) |
JSX transformation |
@babel/preset-typescript |
(when .tsx? files) |
TypeScript syntax stripping |
@babel/preset-flow |
(when flow detected) | Flow type stripping |
Plugins
| Plugin | Purpose |
|---|---|
@babel/plugin-transform-flow-strip-types |
Remove Flow annotations |
babel-plugin-named-asset-import |
Transform named SVG imports to @svgr paths |
@babel/plugin-proposal-decorators |
Stage 2 decorators (legacy: false) |
@babel/plugin-proposal-class-properties |
Class property syntax |
@babel/plugin-proposal-private-methods |
Private class methods |
@babel/plugin-proposal-private-property-in-object |
Private property in checks |
@babel/plugin-proposal-numeric-separator |
1_000_000 syntax |
@babel/plugin-proposal-optional-chaining |
?. operator |
@babel/plugin-proposal-nullish-coalescing-operator |
?? operator |
babel-plugin-macros |
Compile-time code generation (e.g., preval, codegen) |
react-refresh/babel |
React Fast Refresh (development only) |
8.3 ESLint Configuration
eslint-config-react-app extends/uses these plugins:
| Plugin/Config | Rules Category |
|---|---|
eslint-plugin-react |
React-specific rules (JSX, props, hooks usage) |
eslint-plugin-react-hooks |
rules-of-hooks (error), exhaustive-deps (warn) |
eslint-plugin-jsx-a11y |
Accessibility rules for JSX |
eslint-plugin-flowtype |
Flow type annotation rules |
eslint-plugin-import |
Import/export validation |
@typescript-eslint/eslint-plugin |
TypeScript-specific rules (when TS detected) |
confusing-browser-globals |
Restricted global variables |
ESLint runs as a webpack plugin (ESLintPlugin) during compilation, reporting errors in the terminal and error overlay. The eslintFormatter.js utility formats output with file paths and line numbers.
8.4 Testing Infrastructure
Jest Configuration
CRA configures Jest 27 with the following from scripts/test.js and the createJestConfig function:
{
roots: ['<rootDir>/src'],
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],
setupFiles: ['react-app-polyfill/jsdom'],
setupFilesAfterFramework: [paths.testsSetup], // src/setupTests.{js,ts}
testMatch: [
'<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}',
'<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}',
],
testEnvironment: 'jsdom',
transform: {
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': 'config/jest/babelTransform.js',
'^.+\\.css$': 'config/jest/cssTransform.js',
'^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': 'config/jest/fileTransform.js',
},
moduleNameMapper: { '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy' },
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
}
Custom Transforms
| Transform | File | Purpose |
|---|---|---|
babelTransform.js |
*.{js,jsx,ts,tsx,mjs,cjs} |
Runs Babel with babel-preset-react-app in test mode |
cssTransform.js |
*.css |
Returns empty module (CSS is irrelevant in tests) |
fileTransform.js |
All other files | Returns the filename as the module's default export |
Watch Mode
In non-CI environments, Jest runs in --watch mode by default (if git/hg is detected) or --watchAll otherwise. The jest-watch-typeahead plugins add interactive filtering by filename and test name.
Tip
The
src/setupTests.js(or.ts) file runs before every test file. Use it to configure testing-library matchers, mock global APIs, or set up test fixtures. The default template includes@testing-library/jest-domfor DOM assertion matchers liketoBeInTheDocument()andtoHaveTextContent().
8.5 react-dev-utils Module Reference
All 33 utility modules in packages/react-dev-utils/ (91 KB total):
| Module | Exports | Purpose |
|---|---|---|
browsersHelper.js |
checkBrowsers(), defaultBrowsers |
Ensure browserslist is configured; prompts to add defaults |
chalk.js |
Re-export of chalk |
Centralized chalk dependency |
checkRequiredFiles.js |
checkRequiredFiles(files[]) |
Verifies specified files exist; logs missing files |
clearConsole.js |
clearConsole() |
Clears terminal output (respects process.stdout.isTTY) |
crossSpawn.js |
Re-export of cross-spawn |
Cross-platform child process spawning |
errorOverlayMiddleware.js |
Express middleware | Dev server endpoint for launching editor from error overlay |
eslintFormatter.js |
formatter(results) |
Formats ESLint results for terminal; respects ESLINT_NO_DEV_ERRORS |
evalSourceMapMiddleware.js |
Express middleware | Serves source maps for eval'd code in development |
FileSizeReporter.js |
measureFileSizesBeforeBuild(), printFileSizesAfterBuild() |
Reports gzipped file sizes with before/after diff |
formatWebpackMessages.js |
formatWebpackMessages({errors, warnings}) |
Transforms webpack stats into human-readable messages |
getCSSModuleLocalIdent.js |
getCSSModuleLocalIdent(context, ident, name) |
Generates CSS Module class names: [path][name]__[local]--[hash:base64:5] |
getProcessForPort.js |
getProcessForPort(port) |
Identifies which process occupies a given port |
getPublicUrlOrPath.js |
getPublicUrlOrPath(isDev, homepage, envPublicUrl) |
Resolves PUBLIC_URL from environment or package.json homepage |
globby.js |
Re-export of globby |
Centralized globby dependency |
ignoredFiles.js |
ignoredFiles(appSrc) |
Returns regex for webpack watchOptions to ignore node_modules |
immer.js |
Re-export of immer |
Centralized immer dependency |
InlineChunkHtmlPlugin.js |
webpack plugin class | Inlines specified chunks as <script> tags in HTML |
InterpolateHtmlPlugin.js |
webpack plugin class | Replaces %VARIABLE% placeholders in HtmlWebpackPlugin output |
launchEditor.js |
launchEditor(file, line, column) |
Opens file in editor; reads REACT_EDITOR, VISUAL, EDITOR |
ModuleNotFoundPlugin.js |
webpack plugin class | Enhances "module not found" errors with install suggestions |
ModuleScopePlugin.js |
webpack resolver plugin class | Restricts imports to src/ and node_modules/ |
noopServiceWorkerMiddleware.js |
Express middleware | Serves a no-op service worker in development |
openBrowser.js |
openBrowser(url) |
Opens URL in browser; reads BROWSER and BROWSER_ARGS env vars |
openChrome.applescript |
AppleScript | macOS-specific: opens/reuses Chrome tab |
printBuildError.js |
printBuildError(error) |
Formats and prints webpack build errors |
printHostingInstructions.js |
printHostingInstructions(appPackage, publicUrl, publicPath, buildFolder, useYarn) |
Post-build deployment instructions |
redirectServedPathMiddleware.js |
Express middleware | Redirects / to PUBLIC_URL path in dev server |
typescriptFormatter.js |
formatter(issue, useColors) |
Formats TypeScript diagnostic messages |
WebpackDevServerUtils.js |
choosePort(), createCompiler(), prepareProxy(), prepareUrls() |
Dev server utilities: port detection, compiler setup, proxy config, URL resolution |
webpackHotDevClient.js |
webpack entry | Custom HMR client with error overlay integration; reads WDS_SOCKET_* |
getCacheIdentifier.js |
getCacheIdentifier(environment, packages[]) |
Generates cache key from environment and package versions |
ForkTsCheckerWarningWebpackPlugin.js |
webpack plugin class | Converts TypeScript errors to warnings (when TSC_COMPILE_ON_ERROR=true) |
getHttpsConfig.js |
getHttpsConfig() |
Reads SSL_CRT_FILE/SSL_KEY_FILE, validates, returns HTTPS config object |
8.6 react-error-overlay Internals
The error overlay is a standalone package that renders compilation and runtime errors in an iframe injected into the page:
- iframe isolation — Styles and scripts do not conflict with the application
- Source mapping — Uses
source-maplibrary to map transpiled stack traces back to original source - Click-to-source — Clicking a stack frame calls the dev server's launch-editor endpoint
- Runtime hooks — Listens to
window.onerrorandwindow.onunhandledrejection - API —
startReportingRuntimeErrors({onError, filename}),stopReportingRuntimeErrors(),setEditorHandler(handler),dismissBuildError(),reportBuildError(error)
The overlay is included only in development builds. It is imported by webpackHotDevClient.js.
8.7 create-react-app CLI Internals
The CLI package (packages/create-react-app/) handles project scaffolding:
CLI Arguments
| Argument | Description |
|---|---|
<project-directory> |
Required. Name of the directory to create |
--verbose |
Print additional logs during installation |
--info |
Print environment diagnostic information (via envinfo) |
--scripts-version <version> |
Use a specific version of react-scripts (or a custom fork) |
--template <template> |
Use a specific template (default: cra-template) |
--use-pnp |
Enable Yarn Plug'n'Play |
Scaffolding Process
- Validates project name (
validate-npm-package-name) - Creates project directory
- Writes initial
package.json - Detects package manager (npm, yarn, or pnpm)
- Installs
react,react-dom, andreact-scripts(or specified--scripts-version) - Downloads and extracts template package
- Runs
react-scripts initwhich:- Copies template files into the project
- Sets up
package.jsonscripts (start,build,test,eject) - Initializes git repository
- Displays success message with next steps
8.8 Package Manager Detection
The CLI and react-scripts detect the active package manager through multiple signals:
| Signal | Detected Manager |
|---|---|
yarn.lock exists |
Yarn |
pnpm-lock.yaml exists |
pnpm |
npm_config_user_agent contains yarn |
Yarn |
npm_config_user_agent contains pnpm |
pnpm |
| Default | npm |
This affects install commands, lockfile handling, and post-build instructions.
9. Integration Points
9.1 Customization Without Ejecting
Several community tools allow overriding CRA's webpack config without ejecting:
| Tool | Approach |
|---|---|
craco (Create React App Configuration Override) |
craco.config.js with webpack/babel/eslint/jest overrides |
react-app-rewired |
config-overrides.js with webpack-merge-style patching |
customize-cra |
Composable override functions for use with react-app-rewired |
rescripts |
Multi-file override system |
These tools work by intercepting the config before it reaches webpack and applying user-defined modifications.
9.2 Template System
Custom templates are npm packages named cra-template-*. Template structure:
cra-template-{name}/
├── template/ # Files copied into the new project
│ ├── public/
│ ├── src/
│ └── gitignore # Renamed to .gitignore
├── template.json # Additional dependencies and scripts
└── package.json # Template package metadata
template.json supports:
{
"dependencies": {
"additional-package": "^1.0.0"
},
"scripts": {
"custom-script": "echo hello"
}
}
9.3 Monorepo Compatibility
CRA projects can live inside monorepos with caveats:
ModuleScopePluginrestricts imports tosrc/— shared packages must be innode_modules(hoisted) or the plugin must be disabled (via eject or CRACO)babel-loaderonly processes files insideappSrc— shared source outsidesrc/will not be transpiled- Webpack's
resolve.modulesincludes only the project'snode_modules, not parent directories (hoisting handles this for most package managers)
9.4 CI/CD Integration
| CI Platform | Configuration Notes |
|---|---|
| GitHub Actions | Set CI: true in env; caching node_modules and ~/.npm speeds builds |
| GitLab CI | Set CI=true; use npm ci for deterministic installs |
| Travis CI | CI is set automatically; cache: npm recommended |
| Jenkins | Set CI=true explicitly; allocate sufficient memory for webpack |
| Netlify | CI is set automatically; build command is npm run build |
| Vercel | Detects CRA automatically; zero-config deployment |
For memory-constrained CI environments:
NODE_OPTIONS=--max-old-space-size=4096 react-scripts build
Note
When
CI=true, the build process treats all ESLint warnings as errors and fails the build. The test runner also runs all tests once and exits (no watch mode). This is the recommended configuration for all CI pipelines.
9.5 Known Issues and Workarounds
| Issue | Symptoms | Workaround |
|---|---|---|
| Node 17+ OpenSSL (#17216) | ERR_OSSL_EVP_UNSUPPORTED |
NODE_OPTIONS=--openssl-legacy-provider |
.env not loading (#11951, #11954) |
Environment variables undefined at runtime | Ensure REACT_APP_ prefix; restart dev server after changes |
.env changes on restart (#13416) |
Changed variables not picked up | Clear webpack cache: rm -rf node_modules/.cache |
react-scripts not found (#17117) |
Command not recognized | Reinstall: rm -rf node_modules && npm install |
interopRequireDefault (#6398) |
Cannot find module error | Version mismatch; align react-scripts with project dependencies |
.eslintcache generation (#10161) |
File appears in project root | Add .eslintcache to .gitignore |
| Nullish assignment (#9908) | Syntax error on ??= |
Requires react-scripts 5.x or newer Babel config |
| Netlify build failures (#17208) | Build fails on Netlify | Ensure CI=true is set; check Node version compatibility |
9.6 Dependency Version Matrix
Core dependency versions locked by react-scripts 5.1.0:
| Dependency | Version | Purpose |
|---|---|---|
| webpack | 5 | Module bundler |
| Babel | 7 | JavaScript/TypeScript transpiler |
| ESLint | 8 | Code linting |
| Jest | 27 | Test runner |
| PostCSS | 8 | CSS processing |
| Terser | (webpack 5 built-in) | JS minification |
| css-minimizer-webpack-plugin | (latest compatible) | CSS minification |
| html-webpack-plugin | (latest compatible) | HTML generation |
| workbox-webpack-plugin | (latest compatible) | Service worker generation |
| @svgr/webpack | (latest compatible) | SVG-to-React-component |
| dotenv | (latest compatible) | .env file loading |
| dotenv-expand | (latest compatible) | Variable expansion in .env |
Warning
Installing different versions of these dependencies alongside
react-scriptstriggers the preflight version check and may cause unpredictable behavior. If the preflight check blocks startup, investigate the version conflict rather than settingSKIP_PREFLIGHT_CHECK=true.
9.7 Migration Paths
Since CRA is deprecated, the recommended migration targets are:
| Target | Approach | Effort |
|---|---|---|
| Vite | Replace react-scripts with @vitejs/plugin-react; migrate config |
Medium |
| Next.js | Restructure to pages/app router; significant architecture change | High |
| Remix | Restructure to loaders/actions pattern | High |
| Manual webpack | Eject first, then modernize the ejected config | Medium |
| CRACO/react-app-rewired | Stay on CRA with custom overrides (short-term only) | Low |
For Vite migration, the key changes are:
- Replace
react-scriptsscripts withvite/vite build/vitest - Convert
REACT_APP_*env vars toVITE_* - Move
public/index.htmlto project root and add<script type="module" src="/src/index.jsx">tag - Replace
%PUBLIC_URL%with Vite's asset handling - Update
jest.configtovitest.configif using Vitest