Jesse A. Green / notebook

Create React App — Administrator & Developer Guide

Generated May 2026 from facebook/create-react-app at 6254386

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

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

  1. Zero configuration — react-scripts encapsulates all tooling config so end users never touch webpack, Babel, or ESLint directly.
  2. Single dependency — A generated project depends only on react-scripts (plus React itself). All build tools are transitive dependencies.
  3. Eject escape hatch — Running react-scripts eject copies all configuration and scripts into the project, removing the react-scripts dependency permanently.
  4. 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-scripts upgrades 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_UNSUPPORTED errors with webpack's hash functions. The workaround is setting NODE_OPTIONS=--openssl-legacy-provider before 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=true to 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.modules entry and Jest moduleDirectories
  • paths — Maps to webpack resolve.alias entries and Jest moduleNameMapper

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.json fields on startup. Fields like module, moduleResolution, isolatedModules, and jsx are 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:

  1. Sets NODE_ENV=production and BABEL_ENV=production
  2. Loads .env files via config/env.js
  3. Verifies public/index.html and src/index.js exist (checkRequiredFiles)
  4. Measures existing build sizes for diff reporting (FileSizeReporter)
  5. Cleans the BUILD_PATH directory
  6. Copies public/ to build output (excluding index.html)
  7. Runs webpack with production configuration
  8. Reports gzipped file sizes with before/after comparison
  9. 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' with runtimeChunk: 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-template registers 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)

  1. Sets NODE_ENV=development
  2. Verifies required files exist
  3. Detects available port (default 3000, auto-increments via detect-port-alt)
  4. Creates webpack compiler with development config
  5. Starts webpack-dev-server with HMR and error overlay
  6. 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.json or multi-target via src/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)

  1. Sets NODE_ENV=test
  2. Loads .env files (skips .env.local)
  3. Configures Jest with config/jest/ transforms
  4. Runs in watch mode by default (unless CI=true or --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:

  1. Confirms via interactive prompt (unless --force)
  2. Copies config/ directory (webpack, Babel, Jest, env, paths, etc.)
  3. Copies scripts/ directory (start, build, test)
  4. Updates package.json:
    • Moves all react-scripts dependencies to the project
    • Replaces script commands with direct node scripts/*.js calls
    • Removes react-scripts dependency
    • Adds jest, babel, and eslintConfig sections
  5. Stages all changes in git (if repository detected)

Caution

Ejecting is irreversible. Once ejected, the project assumes ownership of all configuration files. Future react-scripts updates (bug fixes, security patches, dependency upgrades) will not apply. Before ejecting, consider alternatives: CRACO, react-app-rewired, or customize-cra for 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.json or jsconfig.json changes

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_CHECK flag 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.0 with 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.css and .module.scss files get locally scoped class names via css-loader modules mode, using the ident pattern [path][name]__[local]--[hash:base64:5] (generated by getCSSModuleLocalIdent)
  • PostCSS Autoprefixer: Automatically adds vendor prefixes based on browserslist
  • Tailwind CSS: Auto-detected when tailwind.config.js exists in project root; PostCSS config includes tailwindcss plugin
  • MiniCssExtractPlugin: Production CSS is extracted to separate files (development uses style-loader for HMR)

Note

Sass support requires installing sass (Dart Sass). The webpack config conditionally enables sass-loader for .scss and .sass files, 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 in REACT_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:

  • localhost and loopback addresses
  • The configured HOST environment 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.json file 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-dom for DOM assertion matchers like toBeInTheDocument() and toHaveTextContent().

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-map library 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.onerror and window.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

  1. Validates project name (validate-npm-package-name)
  2. Creates project directory
  3. Writes initial package.json
  4. Detects package manager (npm, yarn, or pnpm)
  5. Installs react, react-dom, and react-scripts (or specified --scripts-version)
  6. Downloads and extracts template package
  7. Runs react-scripts init which:
    • Copies template files into the project
    • Sets up package.json scripts (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:

  • ModuleScopePlugin restricts imports to src/ — shared packages must be in node_modules (hoisted) or the plugin must be disabled (via eject or CRACO)
  • babel-loader only processes files inside appSrc — shared source outside src/ will not be transpiled
  • Webpack's resolve.modules includes only the project's node_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-scripts triggers the preflight version check and may cause unpredictable behavior. If the preflight check blocks startup, investigate the version conflict rather than setting SKIP_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-scripts scripts with vite / vite build / vitest
  • Convert REACT_APP_* env vars to VITE_*
  • Move public/index.html to project root and add <script type="module" src="/src/index.jsx"> tag
  • Replace %PUBLIC_URL% with Vite's asset handling
  • Update jest.config to vitest.config if using Vitest