Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Create React App — User 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 toolchain for building single-page React applications with zero build configuration. It sets up a modern development environment with Webpack 5, Babel 7, ESLint, Jest, and a development server so you can focus on writing React code rather than wiring together build tools. CRA generates a project scaffold with sensible defaults and provides four core scripts (start, test, build, eject) that cover the full development lifecycle.

Caution

Create React App is officially deprecated. The project maintainers have marked CRA as deprecated and recommend visiting react.dev for modern alternatives such as Next.js, Remix, Gatsby, or Vite-based setups. This guide documents CRA 5.1.0 as-is because millions of existing projects still depend on it. If you are starting a brand-new project, evaluate the alternatives first.

This guide covers create-react-app 5.1.0 and react-scripts 5.1.0, the final release versions. Written from commit 6254386 (2025-02-14) of the source repository.


1. Quick Start

1.1 Create a New Project

The fastest path to a running React app:

npx create-react-app my-app
cd my-app
npm start

Expected output after npx create-react-app my-app:

Creating a new React app in /path/to/my-app.

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...

...

Success! Created my-app at /path/to/my-app
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can't go back!

We suggest that you begin by typing:

  cd my-app
  npm start

Happy hacking!

After npm start, open http://localhost:3000 in your browser to see the running app. The page reloads automatically when you edit source files.

1.2 Create a TypeScript Project

npx create-react-app my-app --template typescript
cd my-app
npm start

This scaffolds the project with .tsx files, a tsconfig.json, and TypeScript type definitions pre-installed.

1.3 What You Get

A freshly created project contains:

my-app/
  node_modules/
  public/
    index.html
    favicon.ico
    manifest.json
    robots.txt
  src/
    App.css
    App.js (or App.tsx)
    App.test.js (or App.test.tsx)
    index.css
    index.js (or index.tsx)
    logo.svg
    reportWebVitals.js (or .ts)
    setupTests.js (or .ts)
  .gitignore
  package.json
  README.md

Note

The public/ directory contains static assets that are served as-is. The src/ directory is where all your application code lives. Webpack only processes files inside src/, so all JS/TS imports must originate there.


2. Installation

2.1 System Requirements

Requirement Minimum Version
Node.js 14 or higher
npm 6 or higher

Verify your versions:

node --version
npm --version

Expected output (example):

v18.19.0
9.2.0

2.2 Installation Methods

Using npx (recommended — always runs the latest version):

npx create-react-app my-app

Using npm global install:

npm install -g create-react-app
create-react-app my-app

Using Yarn:

yarn create react-app my-app

Important

If you previously installed create-react-app globally via npm install -g create-react-app, uninstall it with npm uninstall -g create-react-app to ensure npx always uses the latest version. Stale global installs can cause unexpected behavior.

2.3 Package Manager Detection

CRA automatically detects which package manager you are using. If the npm_config_user_agent environment variable contains "yarn", CRA uses Yarn for dependency installation. Otherwise, it defaults to npm.

2.4 CLI Options

The create-react-app command accepts the following options:

Option Description
--verbose Print additional logging output during installation
--info Print environment debugging information (Node, npm, OS versions)
--scripts-version <version> Use a custom or alternative version of react-scripts
--template <name-or-path> Specify a project template (default: cra-template)
--use-pnp Enable Yarn Plug'n'Play mode for zero-install dependencies

Print environment info:

npx create-react-app --info

Expected output:

Environment Info:

  current version of create-react-app: 5.1.0
  running from /path/to/create-react-app

  System:
    OS: macOS 14.2
    CPU: (10) arm64 Apple M1 Pro
  Binaries:
    Node: 18.19.0
    npm: 9.2.0
    Yarn: 1.22.19
  Browsers:
    Chrome: 120.0
    Firefox: 121.0
    Safari: 17.2

Use a custom template:

npx create-react-app my-app --template typescript
npx create-react-app my-app --template cra-template-pwa

Use verbose output for debugging install failures:

npx create-react-app my-app --verbose

2.5 Available Templates

CRA ships two official templates:

Template Package Name Description
Default (JavaScript) cra-template Standard JavaScript project with React 18
TypeScript cra-template-typescript TypeScript project with tsconfig.json and .tsx files

Community templates follow the naming convention cra-template-*. You can specify any npm package name or a local file path:

npx create-react-app my-app --template file:../my-custom-template

2.6 Updating react-scripts

To update an existing project to a newer version of react-scripts:

npm install react-scripts@latest

Check the react-scripts changelog for breaking changes before upgrading.


3. Configuration

CRA is designed to work with zero configuration. However, it provides extensive customization through environment variables, .env files, browserslist, proxy configuration, and PostCSS/Tailwind integration — all without ejecting.

3.1 Environment Variables

3.1.1 Custom Environment Variables

Any environment variable prefixed with REACT_APP_ is automatically embedded into your application at build time via Webpack's DefinePlugin. Access them in code through process.env:

// In your React code
const apiUrl = process.env.REACT_APP_API_URL;
const version = process.env.REACT_APP_VERSION;

In public/index.html, reference them with %VARIABLE_NAME%:

<title>%REACT_APP_SITE_TITLE%</title>

Warning

Environment variables are embedded at build time, not at runtime. After running npm run build, the values are baked into the static bundle. Changing a .env file requires a rebuild to take effect. Never store secrets in REACT_APP_* variables — they will be visible in the client-side JavaScript bundle.

3.1.2 Built-in Environment Variables

CRA recognizes the following built-in environment variables that control development server behavior and build output:

Variable Default Description
NODE_ENV (auto) Set automatically: development for start, test for test, production for build
PORT 3000 Port for the development server
HOST 0.0.0.0 Host binding for the development server
HTTPS false Set to true to run the dev server over HTTPS
SSL_CRT_FILE (none) Path to a custom SSL certificate file (used with HTTPS=true)
SSL_KEY_FILE (none) Path to a custom SSL key file (used with HTTPS=true)
BROWSER (system default) Browser to open on npm start. Set to none to disable auto-open
PUBLIC_URL / URL prefix for assets. Use when hosting from a subdirectory
BUILD_PATH build Output directory for npm run build
GENERATE_SOURCEMAP true Set to false to skip source map generation in production builds
INLINE_RUNTIME_CHUNK true Set to false to prevent inlining the Webpack runtime chunk
IMAGE_INLINE_SIZE_LIMIT 10000 Images smaller than this (in bytes) are inlined as data URIs
FAST_REFRESH true Enable/disable React Fast Refresh in development
DISABLE_ESLINT_PLUGIN false Set to true to completely disable the ESLint webpack plugin
TSC_COMPILE_ON_ERROR false Set to true to allow building even when TypeScript type errors exist
ESLINT_NO_DEV_ERRORS false Set to true to downgrade ESLint errors to warnings in development
NODE_PATH (none) Additional directories to resolve modules from (colon-separated)
WDS_SOCKET_HOST (auto) WebSocket host for the dev server hot-reload connection
WDS_SOCKET_PATH (auto) WebSocket path for the dev server hot-reload connection
WDS_SOCKET_PORT (auto) WebSocket port for the dev server hot-reload connection
DANGEROUSLY_DISABLE_HOST_CHECK false Disables the host check on the dev server (security risk)

3.1.3 Setting Environment Variables

On the command line (Linux/macOS):

PORT=4000 BROWSER=none npm start

On the command line (Windows cmd):

set PORT=4000 && set BROWSER=none && npm start

On the command line (Windows PowerShell):

$env:PORT=4000; $env:BROWSER="none"; npm start

Tip

Use the cross-env package for cross-platform environment variable setting in package.json scripts: "start": "cross-env PORT=4000 react-scripts start".

3.2 .env Files

CRA supports .env files for defining environment variables declaratively. Files are loaded in the following priority order (highest to lowest):

  1. .env.{NODE_ENV}.local — Local overrides for a specific environment
  2. .env.local — Local overrides (loaded in all environments except test)
  3. .env.{NODE_ENV} — Environment-specific defaults
  4. .env — Global defaults

Where {NODE_ENV} is development, test, or production.

Example .env file:

REACT_APP_API_URL=https://api.example.com
REACT_APP_VERSION=1.2.3
REACT_APP_FEATURE_FLAG=true

Example .env.development:

REACT_APP_API_URL=http://localhost:8080
REACT_APP_DEBUG=true

Example .env.production:

REACT_APP_API_URL=https://api.production.com
REACT_APP_SENTRY_DSN=https://[email removed]/12345

Note

Variables defined in higher-priority files override those in lower-priority files. A variable set in .env.development.local overrides the same variable in .env. The .env.local file is automatically added to .gitignore in new CRA projects — use it for machine-specific overrides you don't want committed.

Variable expansion is supported within .env files:

REACT_APP_DOMAIN=example.com
REACT_APP_API_URL=https://api.$REACT_APP_DOMAIN/v1

3.3 Browserslist

CRA uses the browserslist configuration in package.json to determine which browsers to target for CSS autoprefixing and JavaScript transpilation:

{
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

The production list controls which transforms Babel applies and which CSS vendor prefixes PostCSS adds. The development list keeps the dev bundle lighter by targeting only modern browsers.

3.4 HTTPS in Development

Run the development server over HTTPS with a self-signed certificate:

HTTPS=true npm start

Your browser will warn about the untrusted certificate. To use a custom trusted certificate:

HTTPS=true SSL_CRT_FILE=./certs/server.crt SSL_KEY_FILE=./certs/server.key npm start

This is useful when working with APIs that enforce secure contexts or when testing service workers locally.

3.5 Proxy Configuration

3.5.1 Simple Proxy

For forwarding API requests to a backend during development, add a proxy field to package.json:

{
  "proxy": "http://localhost:4000"
}

Any request to a path not served by the dev server (i.e., not a static file) will be proxied to http://localhost:4000. For example, fetch('/api/users') in your code will be proxied to http://localhost:4000/api/users.

3.5.2 Advanced Proxy with setupProxy.js

For more control, create src/setupProxy.js:

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:4000',
      changeOrigin: true,
    })
  );
  app.use(
    '/ws',
    createProxyMiddleware({
      target: 'ws://localhost:4001',
      ws: true,
    })
  );
};

Important

The src/setupProxy.js file is only used during development. It is not included in the production build. You must configure your production server's reverse proxy separately. The file must use require() syntax (CommonJS), not ES module import.

3.6 Absolute Imports

Configure absolute imports by setting baseUrl in jsconfig.json (JavaScript) or tsconfig.json (TypeScript):

jsconfig.json:

{
  "compilerOptions": {
    "baseUrl": "src"
  }
}

Now you can import from src/ without relative paths:

// Instead of: import Button from '../../../components/Button';
import Button from 'components/Button';

3.7 Tailwind CSS

CRA supports Tailwind CSS natively. If a tailwind.config.js file exists at the project root, CRA's PostCSS pipeline automatically includes the Tailwind plugin.

  1. Install Tailwind:
npm install -D tailwindcss
npx tailwindcss init
  1. Configure tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};
  1. Add Tailwind directives to src/index.css:
@tailwind base;
@tailwind components;
@tailwind utilities;

3.8 PostCSS and Autoprefixer

CRA includes PostCSS with autoprefixer by default. Vendor prefixes are automatically added based on your browserslist configuration. No additional setup is needed. For example, if you write:

.container {
  display: flex;
  user-select: none;
}

The output CSS will include the necessary vendor prefixes for your target browsers automatically.


4. Core Usage

4.1 The Four Scripts

Every CRA project comes with four scripts defined in package.json. These are provided by react-scripts and cover the entire development lifecycle.

4.1.1 npm start — Development Server

npm start

Launches a development server at http://localhost:3000 with:

  • Hot Module Replacement via React Fast Refresh — edits to React components update in the browser without losing component state
  • ESLint overlay — lint errors and warnings appear directly in the browser
  • Error overlay via react-error-overlay — runtime errors display a full-screen overlay with the stack trace, clickable to open the source in your editor
  • Automatic browser opening — your default browser opens to the app URL

Expected terminal output:

Compiled successfully!

You can now view my-app in the browser.

  Local:            http://localhost:3000
  On Your Network:  http://192.168.1.100:3000

Note that the development build is not optimized.
To create a production build, use npm run build.

webpack compiled successfully

To change the port:

PORT=4000 npm start

To prevent the browser from auto-opening:

BROWSER=none npm start

4.1.2 npm test — Test Runner

npm test

Launches Jest in interactive watch mode. By default, it only runs tests related to files changed since the last commit, making the feedback loop fast.

Expected terminal output:

No tests found related to files changed since last commit.
Press `a` to run all tests, or run Jest with `--watchAll`.

Watch Usage
 › Press a to run all tests.
 › Press f to run only failed tests.
 › Press q to quit watch mode.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press Enter to trigger a test run.

CRA's test setup includes:

  • @testing-library/react — the recommended library for testing React components
  • jest-dom matchers — via src/setupTests.js which imports @testing-library/jest-dom
  • jsdom environment — tests run in a simulated browser environment

Example test file (src/App.test.js):

import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

Running tests in CI (non-interactive mode):

CI=true npm test

In CI mode, Jest runs all tests once without watch mode and exits with a non-zero code if any test fails.

Running with coverage:

CI=true npm test -- --coverage

This generates a coverage report in the coverage/ directory.

Tip

The src/setupTests.js file runs before every test. Use it to configure global test utilities, mock browser APIs, or import custom matchers. CRA scaffolds it with import '@testing-library/jest-dom'; by default.

4.1.3 npm run build — Production Build

npm run build

Creates an optimized production build in the build/ directory (configurable via BUILD_PATH). The build process:

  • Minifies JavaScript and CSS
  • Generates content-hashed filenames for long-term caching (e.g., main.a1b2c3d4.js)
  • Inlines small images as data URIs (below IMAGE_INLINE_SIZE_LIMIT, default 10KB)
  • Generates source maps (unless GENERATE_SOURCEMAP=false)
  • Produces a build/asset-manifest.json listing all generated files

Expected terminal output:

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  46.61 kB  build/static/js/main.a1b2c3d4.js
  1.78 kB   build/static/js/787.f4c22a95.chunk.js
  541 B     build/static/css/main.073c9b0a.css

The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.

The build folder is ready to be deployed.
You may serve it with a static server:

  serve -s build

Find out more about deployment at https://cra.link/deployment

Customizing the build output directory:

BUILD_PATH=dist npm run build

Skipping source maps (reduces build size and hides source code):

GENERATE_SOURCEMAP=false npm run build

Deploying to a subdirectory (e.g., https://example.com/my-app/):

Set homepage in package.json:

{
  "homepage": "https://example.com/my-app"
}

Or use the PUBLIC_URL environment variable:

PUBLIC_URL=/my-app npm run build

4.1.4 npm run eject — Eject Configuration

npm run eject

Caution

Ejecting is a one-way operation. Once you eject, you cannot go back. This copies all configuration files (Webpack, Babel, ESLint, Jest) and transitive dependencies directly into your project. You become fully responsible for maintaining them. Consider this a last resort — most customizations can be achieved through environment variables, .env files, src/setupProxy.js, or packages like craco and react-app-rewired without ejecting.

After ejecting, your project gains:

  • config/ directory with Webpack, Jest, and environment configuration
  • scripts/ directory with start.js, build.js, and test.js
  • All hidden dependencies added directly to package.json

4.2 Supported File Types

CRA's Webpack configuration handles a wide range of file types out of the box:

File Extension Handling
.js, .jsx Babel-transpiled JavaScript with React JSX support
.ts, .tsx TypeScript compiled with Babel (type-checking via tsc)
.mjs ES modules (processed by Webpack)
.css Standard CSS, injected via style-loader in dev, extracted in production
.module.css CSS Modules with locally scoped class names
.scss, .sass Sass/SCSS (requires sass package)
.module.scss, .module.sass CSS Modules with Sass
.svg Importable as React component or URL
.json Parsed and importable as a JavaScript object
.bmp, .gif, .jpg, .jpeg, .png Inlined as data URI if below IMAGE_INLINE_SIZE_LIMIT (10KB), otherwise emitted as file
.avif AVIF images, same inlining behavior as above

4.3 CSS and Styling

4.3.1 Standard CSS

Import CSS files directly in your components:

import './App.css';

function App() {
  return <div className="app-container">Hello</div>;
}
/* App.css */
.app-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

4.3.2 CSS Modules

Name your CSS file with .module.css to enable locally scoped class names that avoid naming collisions:

import styles from './Button.module.css';

function Button() {
  return <button className={styles.primary}>Click me</button>;
}
/* Button.module.css */
.primary {
  background-color: #007bff;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
}

At build time, the class name primary is transformed to something like Button_primary__x1y2z to guarantee uniqueness.

4.3.3 Sass/SCSS

Install the sass package to enable Sass support:

npm install sass

Then import .scss or .sass files:

import './App.scss';
// App.scss
$primary-color: #282c34;

.app-container {
  background-color: $primary-color;
  min-height: 100vh;

  .header {
    font-size: 2rem;
    color: white;
  }
}

4.3.4 CSS Modules with Sass

Combine CSS Modules and Sass by using the .module.scss or .module.sass extension:

import styles from './Card.module.scss';

function Card({ children }) {
  return <div className={styles.card}>{children}</div>;
}
// Card.module.scss
$border-radius: 8px;
$shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

.card {
  border-radius: $border-radius;
  box-shadow: $shadow;
  padding: 16px;
}

4.4 Working with Assets

4.4.1 Images

Import images directly. Small images (below IMAGE_INLINE_SIZE_LIMIT, default 10,000 bytes) are inlined as data URIs; larger ones are emitted as files with content hashes:

import logo from './logo.png';
import icon from './small-icon.png'; // < 10KB, will be inlined

function Header() {
  return (
    <div>
      <img src={logo} alt="Logo" />
      <img src={icon} alt="Icon" />
    </div>
  );
}

Supported formats: BMP, GIF, JPEG, PNG, AVIF.

To change the inlining threshold:

# .env
IMAGE_INLINE_SIZE_LIMIT=5000

4.4.2 SVGs

SVGs can be imported two ways:

As a URL (standard image import):

import logo from './logo.svg';

function App() {
  return <img src={logo} alt="Logo" />;
}

As a React component (inline SVG with full styling control):

import { ReactComponent as Logo } from './logo.svg';

function App() {
  return <Logo className="app-logo" fill="currentColor" />;
}

The ReactComponent named export uses SVGR under the hood to transform the SVG file into a React component.

Tip

Importing SVGs as React components gives you full CSS and prop control over the SVG elements. Use this approach when you need to change colors, sizes, or animate SVG paths dynamically. Use the URL import when you just need a static image.

4.4.3 JSON

JSON files can be imported directly:

import config from './config.json';

console.log(config.apiEndpoint);

4.4.4 The public/ Directory

Files in the public/ directory are served at the root URL and are not processed by Webpack. Use this for:

  • index.html — the main HTML template
  • favicon.ico, manifest.json, robots.txt
  • Assets that need a fixed URL (e.g., og:image meta tags)

Reference public assets in HTML using %PUBLIC_URL%:

<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />

Reference public assets in JavaScript using process.env.PUBLIC_URL:

<img src={process.env.PUBLIC_URL + '/images/banner.png'} alt="Banner" />

Note

Files in public/ are not minified, not content-hashed, and will cause 404 errors if missing at runtime with no compile-time warning. Prefer importing assets through src/ whenever possible — Webpack provides compile-time errors for missing files, content-hash caching, and size optimization.

4.5 TypeScript

4.5.1 Starting with TypeScript

Create a new TypeScript project:

npx create-react-app my-app --template typescript

This generates a project with .tsx source files, a tsconfig.json, and all necessary type definitions pre-installed (@types/react, @types/react-dom, @types/jest, @types/node).

4.5.2 Adding TypeScript to an Existing Project

  1. Install TypeScript and type definitions:
npm install --save typescript @types/node @types/react @types/react-dom @types/jest
  1. Rename any file to .tsx (or .ts for non-JSX files).

  2. Restart the development server. CRA automatically generates a tsconfig.json if one doesn't exist.

4.5.3 TypeScript Configuration

CRA manages certain tsconfig.json fields and will overwrite them on start. You can customize other fields freely:

{
  "compilerOptions": {
    "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",
    "baseUrl": "src"
  },
  "include": ["src"]
}

Building despite type errors:

TSC_COMPILE_ON_ERROR=true npm start

This allows the development server to run even when there are TypeScript type errors, showing them as warnings instead of failures.

4.6 Flow

CRA supports Flow type annotations out of the box. Add a // @flow comment at the top of any file:

// @flow
import React from 'react';

type Props = {
  name: string,
  age: number,
};

function Greeting({ name, age }: Props) {
  return <p>Hello {name}, you are {age} years old.</p>;
}

export default Greeting;

Babel strips Flow annotations during compilation. No additional configuration is required.


5. Common Tasks

5.1 Adding a Router

CRA does not include routing by default. Install React Router:

npm install react-router-dom
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

When deploying a single-page app with client-side routing, configure your server to serve index.html for all routes. Otherwise, direct URL access to /about will return a 404.

5.2 Adding Global State

Using React Context (built-in):

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}

5.3 Code Splitting

CRA supports code splitting via React.lazy and Suspense:

import React, { Suspense, lazy } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

Each lazy() import creates a separate JavaScript chunk that is loaded on demand.

5.4 Fetching Data

import { useState, useEffect } from 'react';

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('/api/users')
      .then(res => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.json();
      })
      .then(data => setUsers(data))
      .catch(err => setError(err.message))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

During development, use the proxy field in package.json to avoid CORS issues when your API runs on a different port.

5.5 Progressive Web App (PWA)

CRA includes workbox-webpack-plugin for generating a service worker. To enable offline support:

  1. In src/index.js, change the service worker registration:
// Change this:
serviceWorkerRegistration.unregister();

// To this:
serviceWorkerRegistration.register();
  1. The service worker caches your app shell and static assets. Subsequent visits load instantly from cache.

Warning

Enabling the service worker means users may see stale content until the new service worker activates (typically on the next page load). During development, always use unregister() to avoid caching development builds.

5.6 Custom Error Overlay

CRA includes react-error-overlay, which displays a full-screen error overlay in development when:

  • A JavaScript runtime error occurs
  • An unhandled promise rejection happens
  • A compilation error exists

The overlay shows the error message, stack trace, and clickable source locations that open the file in your editor. This behavior is automatic in development and is stripped from production builds.

5.7 ESLint Configuration

CRA includes eslint-config-react-app, which bundles rules from:

  • ESLint core rules
  • React-specific rules (eslint-plugin-react)
  • JSX accessibility rules (eslint-plugin-jsx-a11y)
  • React Hooks rules (eslint-plugin-react-hooks)
  • Flow type rules (when Flow is used)
  • TypeScript rules (when TypeScript is used)

Lint errors appear in the terminal and as a browser overlay during development.

Disabling ESLint entirely:

# .env
DISABLE_ESLINT_PLUGIN=true

Downgrading ESLint errors to warnings in development:

# .env
ESLINT_NO_DEV_ERRORS=true

Extending ESLint rules in package.json:

{
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ],
    "rules": {
      "no-console": "warn",
      "react/prop-types": "error"
    }
  }
}

5.8 Testing Patterns

5.8.1 Testing User Interactions

import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('increments counter on button click', () => {
  render(<Counter />);
  const button = screen.getByRole('button', { name: /increment/i });
  fireEvent.click(button);
  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

5.8.2 Testing Async Operations

import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';

test('displays user data after loading', async () => {
  render(<UserProfile userId="123" />);
  
  expect(screen.getByText(/loading/i)).toBeInTheDocument();
  
  await waitFor(() => {
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

5.8.3 Mocking API Calls

// src/__mocks__/axios.js
export default {
  get: jest.fn(() => Promise.resolve({ data: {} })),
  post: jest.fn(() => Promise.resolve({ data: {} })),
};
import axios from 'axios';
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';

jest.mock('axios');

test('fetches and displays users', async () => {
  axios.get.mockResolvedValue({
    data: [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }],
  });

  render(<UserList />);

  await waitFor(() => {
    expect(screen.getByText('Alice')).toBeInTheDocument();
    expect(screen.getByText('Bob')).toBeInTheDocument();
  });
});

5.8.4 Snapshot Testing

import { render } from '@testing-library/react';
import App from './App';

test('matches snapshot', () => {
  const { container } = render(<App />);
  expect(container).toMatchSnapshot();
});

Snapshots are stored in __snapshots__/ directories. Update them with:

npm test -- --updateSnapshot

5.8.5 Coverage Reports

CI=true npm test -- --coverage

Expected output:

----------|---------|----------|---------|---------|
File      | % Stmts | % Branch | % Funcs | % Lines |
----------|---------|----------|---------|---------|
All files |   85.71 |    66.67 |   83.33 |   85.71 |
 App.js   |     100 |      100 |     100 |     100 |
 utils.js |   71.43 |    33.33 |   66.67 |   71.43 |
----------|---------|----------|---------|---------|

5.9 Deployment

5.9.1 Static Server

The simplest way to serve a production build:

npm install -g serve
serve -s build

The -s flag enables single-page app mode, rewriting all routes to index.html.

5.9.2 Deploying to a Subdirectory

Set homepage in package.json:

{
  "homepage": "https://mysite.com/app"
}

This sets PUBLIC_URL to /app, and all asset paths will be prefixed accordingly.

5.9.3 Deploying to GitHub Pages

  1. Install the deployment package:
npm install --save-dev gh-pages
  1. Add to package.json:
{
  "homepage": "https://<username>.github.io/<repo-name>",
  "scripts": {
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  }
}
  1. Deploy:
npm run deploy

5.9.4 Environment-Specific Builds

# Production build (default)
npm run build

# Build with a custom API URL
REACT_APP_API_URL=https://staging-api.example.com npm run build

# Build without source maps
GENERATE_SOURCEMAP=false npm run build

# Build to a custom directory
BUILD_PATH=dist npm run build

5.10 Using Web Workers

CRA does not have built-in Web Worker support in Webpack configuration, but you can use them with a workaround:

const worker = new Worker(new URL('./worker.js', import.meta.url));

worker.postMessage({ type: 'START', data: largeDataSet });
worker.onmessage = (event) => {
  console.log('Worker result:', event.data);
};

5.11 Adding Custom Fonts

Via CSS imports:

/* src/index.css */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');

body {
  font-family: 'Inter', sans-serif;
}

Via local font files:

/* src/fonts.css */
@font-face {
  font-family: 'CustomFont';
  src: url('./fonts/CustomFont-Regular.woff2') format('woff2'),
       url('./fonts/CustomFont-Regular.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}

Place font files in src/fonts/ so Webpack processes them.

5.12 Analyzing Bundle Size

Add the source-map-explorer to inspect what contributes to your bundle:

npm install --save-dev source-map-explorer

Add a script to package.json:

{
  "scripts": {
    "analyze": "source-map-explorer 'build/static/js/*.js'"
  }
}

Run:

npm run build
npm run analyze

This opens a treemap visualization showing the size contribution of each module.


6. Troubleshooting

6.1 'react-scripts' Is Not Recognized

Symptom: Running npm start fails with 'react-scripts' is not recognized as an internal or external command or command not found: react-scripts.

Cause: The node_modules directory is missing or corrupted.

Fix:

rm -rf node_modules
rm package-lock.json
npm install

On Windows:

rmdir /s /q node_modules
del package-lock.json
npm install

This issue is tracked in #17117.

6.2 ERR_OSSL_EVP_UNSUPPORTED on Node 17+

Symptom: Build or start fails with Error: error:0308010C:digital envelope routines::unsupported.

Cause: Node.js 17+ uses OpenSSL 3 which removed support for certain legacy algorithms that Webpack 5 / react-scripts depends on.

Fix (recommended) — set the OpenSSL legacy provider:

NODE_OPTIONS=--openssl-legacy-provider npm start

Or add it to your package.json scripts:

{
  "scripts": {
    "start": "react-scripts start",
    "start:legacy": "NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
    "build:legacy": "NODE_OPTIONS=--openssl-legacy-provider react-scripts build"
  }
}

This issue is extensively discussed in #17216. The most reliable long-term fix is to use Node 16 or 18 LTS.

Tip

Use a Node version manager like nvm or fnm to easily switch between Node versions per project.

6.3 .env Variables Not Working

Symptom: Environment variables defined in .env files are undefined in the app.

Causes and fixes:

  1. Missing REACT_APP_ prefix — Only variables starting with REACT_APP_ are injected. API_URL=... will not work; use REACT_APP_API_URL=....

  2. Server not restarted — .env files are only read when the dev server starts. After editing .env, stop and restart:

# Stop the server (Ctrl+C), then:
npm start
  1. Wrong file loaded — Check the loading priority. A value in .env.development.local overrides .env.

  2. Syntax errors — Each line must be KEY=value with no spaces around =. No quotes needed unless the value contains spaces.

These issues appear across #13416, #11951, and #11954.

6.4 Cannot Find Module 'react-scripts/scripts/...' or interopRequireDefault

Symptom: Start or build fails with Cannot find module errors referencing internal react-scripts paths.

Cause: Corrupted node_modules or version mismatch between dependencies.

Fix:

rm -rf node_modules
rm package-lock.json
npm cache clean --force
npm install

If persisting, verify react-scripts is in dependencies (not devDependencies) in package.json:

{
  "dependencies": {
    "react-scripts": "5.1.0"
  }
}

Related: #6398.

6.5 ESLint Cache Constantly Regenerated

Symptom: The .eslintcache file keeps appearing in your project root, cluttering version control.

Fix: Add .eslintcache to your .gitignore:

# .gitignore
.eslintcache

CRA's generated .gitignore should already include this, but older projects may not have it. Related: #10161.

6.6 Build Fails on Netlify / CI Platforms

Symptom: npm run build fails in CI with errors that do not reproduce locally.

Common causes:

  1. Case sensitivity — Linux CI servers have case-sensitive file systems. import Header from './header' fails if the file is Header.js. Always match exact casing.

  2. Missing dependencies — Ensure all dependencies are in dependencies (not just devDependencies) if they're needed at build time.

  3. Node version mismatch — Pin your Node version in CI. Add an .nvmrc file:

18.19.0

Or set the Node version in your CI config. Related: #17208.

Important

CI environments typically set CI=true automatically. In this mode, ESLint warnings are treated as errors and will fail the build. Fix all warnings locally, or set ESLINT_NO_DEV_ERRORS=true if you need a temporary workaround. Setting CI=false in your CI environment disables this behavior but is not recommended.

6.7 Optional Chaining / Nullish Coalescing Not Working

Symptom: Syntax like obj?.property or value ?? default causes parse errors.

Cause: Older versions of react-scripts (pre-4.0) do not include Babel plugins for these operators.

Fix: Upgrade to react-scripts 4.0 or later:

npm install react-scripts@5.1.0

Related: #9908.

6.8 Dev Server Occupies Port 3000

Symptom: Something is already running on port 3000 when starting the dev server.

Fix (use a different port):

PORT=3001 npm start

Fix (find and kill the process):

# macOS/Linux
lsof -i :3000
kill -9 <PID>

# Windows
netstat -ano | findstr :3000
taskkill /PID <PID> /F

6.9 Hot Reload Not Working

Symptom: Changes to files do not trigger a browser refresh.

Common causes and fixes:

  1. File is outside src/ — Webpack only watches files inside src/. Move the file there.

  2. FAST_REFRESH=false — Check your .env files; remove or set FAST_REFRESH=true.

  3. File system event limits (Linux):

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
  1. Network file system — Webpack's file watcher may not work on mounted drives or Docker volumes. Use polling:
CHOKIDAR_USEPOLLING=true npm start

6.10 Memory Issues During Build

Symptom: Build crashes with JavaScript heap out of memory.

Fix — increase Node's memory limit:

NODE_OPTIONS=--max-old-space-size=4096 npm run build

For very large projects, you may need 8192 (8GB).


7. FAQ

General

Q: Is Create React App still maintained? A: No. CRA is officially deprecated as of the final 5.1.0 release. The React team recommends using frameworks like Next.js, Remix, or Vite-based setups for new projects. Existing CRA projects continue to work but will not receive new features or security patches to react-scripts.

Q: Can I use React 18 with CRA? A: Yes. CRA 5.x supports React 18 out of the box. The default template scaffolds with ReactDOM.createRoot() for concurrent features.

Q: What build tool does CRA use? A: CRA uses Webpack 5 for bundling and Babel 7 (with babel-preset-react-app) for JavaScript/TypeScript transpilation. The development server is webpack-dev-server.

Q: Is CRA a monorepo? A: Yes. The CRA source repository is a monorepo managed with Lerna. Packages live in packages/*, including create-react-app (the CLI), react-scripts (the build tooling), babel-preset-react-app, eslint-config-react-app, and others. As an end user, you interact with the create-react-app CLI and the react-scripts package.

Configuration

Q: Can I customize the Webpack config without ejecting? A: CRA does not expose the Webpack config directly. Community tools like CRACO (@craco/craco) and react-app-rewired allow you to override Webpack, Babel, and ESLint configuration without ejecting. These are third-party solutions and not officially supported.

Q: How do I add environment-specific configuration? A: Use .env files with the NODE_ENV suffix: .env.development, .env.production, .env.test. Variables in these files are loaded automatically based on which script you run (start = development, build = production, test = test).

Q: Can I change the output directory for builds? A: Yes. Set the BUILD_PATH environment variable:

BUILD_PATH=dist npm run build

Q: How do I use a different port for the dev server? A: Set the PORT environment variable:

PORT=8080 npm start

Q: How do I serve my app from a subdirectory? A: Set homepage in package.json to the full URL or path prefix:

{
  "homepage": "/my-app"
}

All asset paths will be prefixed with /my-app.

Styling

Q: Does CRA support CSS-in-JS libraries? A: CRA does not include any CSS-in-JS library by default, but you can install and use any library (styled-components, Emotion, etc.) without additional configuration.

Q: How do CSS Modules work in CRA? A: Name your CSS file with a .module.css (or .module.scss) extension. Import it and use the exported object for class names. Each class name is locally scoped and uniquely generated at build time to prevent collisions.

Q: Does CRA include PostCSS? A: Yes. CRA includes PostCSS with autoprefixer enabled by default. Vendor prefixes are added automatically based on your browserslist configuration. If a tailwind.config.js exists, the Tailwind PostCSS plugin is also included automatically.

TypeScript

Q: Does CRA type-check at build time? A: CRA uses Babel to strip TypeScript types during compilation (it does not run tsc for compilation). Type errors appear in the terminal and browser overlay during development via a separate type-checking process, but do not block the build by default. Set TSC_COMPILE_ON_ERROR=false if you want strict type checking to block compilation.

Q: Can I mix JavaScript and TypeScript files? A: Yes. CRA supports .js, .jsx, .ts, and .tsx files in the same project. You can incrementally adopt TypeScript by renaming files one at a time.

Q: Does CRA overwrite my tsconfig.json? A: CRA manages certain compiler options that it depends on (like isolatedModules, noEmit, jsx). It will reset these on startup if you change them. Other settings like strict, baseUrl, and custom paths are yours to configure.

Testing

Q: What testing library does CRA include? A: CRA includes Jest as the test runner and scaffolds projects with @testing-library/react and @testing-library/jest-dom for component testing.

Q: How do I run a single test file? A: In watch mode, press p then type a regex matching the file name. Or run:

npm test -- --testPathPattern="App.test"

Q: How do I run tests in CI without watch mode? A: Set CI=true:

CI=true npm test

Q: Where does CRA look for test files? A: Jest finds tests in any of these patterns:

  • Files in __tests__/ directories
  • Files ending with .test.js or .test.tsx
  • Files ending with .spec.js or .spec.tsx

Build and Deploy

Q: How large is a typical CRA production build? A: A minimal CRA app produces approximately 40-50KB of gzipped JavaScript. The size grows with your application code and dependencies.

Q: Can I disable source maps in production? A: Yes:

GENERATE_SOURCEMAP=false npm run build

This reduces build output size and prevents exposing source code on production servers.

Q: What is the asset-manifest.json file? A: After npm run build, CRA generates build/asset-manifest.json containing a mapping of logical file names to their content-hashed counterparts. Server-side rendering setups and deployment tools use this to reference the correct file names.

Q: Does CRA support server-side rendering? A: CRA is designed for client-side single-page applications. It does not include server-side rendering. For SSR, consider Next.js or use ReactDOMServer with a custom server setup (which requires ejecting or using CRACO).

Progressive Web App

Q: Is the service worker enabled by default? A: No. New CRA projects call serviceWorkerRegistration.unregister() by default. Change this to serviceWorkerRegistration.register() to enable offline support.

Q: What caching strategy does the service worker use? A: CRA uses workbox-webpack-plugin which generates a precache manifest of your build output. All static assets are cached on first load and served from cache on subsequent visits.

Ejecting

Q: Can I undo an eject? A: No. Ejecting is irreversible. Once you eject, all configuration files are copied into your project and the react-scripts dependency is removed. There is no automated way to reverse this. Use version control to preserve a pre-eject snapshot.

Q: What files does eject create? A: Ejecting creates:

  • config/webpack.config.js — full Webpack configuration
  • config/webpackDevServer.config.js — dev server configuration
  • config/jest/ — Jest transform and configuration
  • config/env.js, config/paths.js, config/modules.js — environment and path utilities
  • scripts/start.js, scripts/build.js, scripts/test.js — the three main scripts

Q: Are there alternatives to ejecting? A: Yes. CRACO and react-app-rewired allow you to override CRA's Webpack, Babel, and ESLint configuration without ejecting. Environment variables also cover many common customization needs.

For full configuration reference, security considerations, architecture internals, and deployment hardening, see the Admin Guide.