ReactProject Setup with Create React App

Project Setup with Create React App

Create React App (CRA) was the official way to bootstrap React applications from 2016 to around 2022. It gave developers a zero-configuration, batteries-included starter that handled Webpack, Babel, ESLint, and testing setup behind the scenes. For years, nearly every React tutorial started with npx create-react-app my-app.

Today, Vite is the recommended choice for new projects. However, CRA is still maintained and you will encounter it in existing codebases, bootcamp curricula, and older tutorials. Understanding CRA is therefore still practically important.

Warning
The React team officially recommends Vite, Next.js, or Remix for new projects. CRA receives security patches but is not actively developed with new features. If you are starting from scratch, use Vite (see the previous page).
Creating a CRA Project

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

npx downloads and runs create-react-app without installing it globally. After a minute or two (CRA installs many dependencies), your app is running at http://localhost:3000.

For TypeScript:

Bash
npx create-react-app my-app --template typescript
Understanding the Generated Structure

Bash
my-app/
├── public/
│   ├── favicon.ico
│   ├── index.html          # the HTML shell (do not delete the #root div)
│   ├── manifest.json       # PWA manifest
│   └── robots.txt
├── src/
│   ├── App.css             # styles for the App component
│   ├── App.js              # root App component
│   ├── App.test.js         # example test
│   ├── index.css           # global styles
│   ├── index.js            # entry point — renders <App> into #root
│   ├── logo.svg            # the React spinning logo
│   ├── reportWebVitals.js  # optional performance measuring
│   └── setupTests.js       # configures Jest + React Testing Library
├── .gitignore
├── package.json
└── README.md
Key Files Explained

public/index.html — The HTML shell. CRA injects the compiled JavaScript bundle here automatically. The crucial element is the <div id="root"> — this is where React mounts:

HTML
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    <!-- CRA injects the bundle script tag here automatically -->
  </body>
</html>

src/index.js — The React entry point. In CRA, this uses the older ReactDOM.render in React 17 projects, but newer CRA projects use createRoot:

JSX
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)

// Measure performance (optional — remove if not needed)
reportWebVitals()

src/App.js — The root component. Delete the boilerplate and start building here.

Available Scripts
  • npm start — start the development server at http://localhost:3000 with hot reload

  • npm run build — create an optimized production build in the build/ folder

  • npm test — run tests in watch mode using Jest and React Testing Library

  • npm run eject — permanently exposes all configuration files (Webpack, Babel, ESLint). One-way operation — cannot be undone.

Warning
Never run `npm run eject` unless you have a specific, compelling reason. Ejecting hands you thousands of lines of raw Webpack and Babel configuration to maintain yourself. Almost every customization need can be met without ejecting, using CRACO or by migrating to Vite.
CRA's Build Limitations

CRA uses Webpack under the hood. On small projects this is fine. On larger codebases, the pain points become significant:

  • Slow cold starts — bundling everything upfront means waiting 30–120 seconds for the dev server on large projects

  • Slow HMR — replacing a module requires re-bundling parts of the dependency graph. On big apps, a file save can take several seconds to reflect in the browser

  • Large node_modules — CRA installs 1,000+ packages by default

  • Webpack config hidden — you cannot customize the build without ejecting or using workarounds like CRACO

  • No SSR — CRA only produces client-rendered apps

Note
CRA's hidden configuration is by design — "zero config" means less to learn and maintain. But this becomes a constraint as your project grows. Vite exposes its config in a small, readable `vite.config.js`.
Migrating from CRA to Vite

If you have an existing CRA project and want to move to Vite, the migration is straightforward for most projects:

Bash
# 1. Install Vite and the React plugin
npm install --save-dev vite @vitejs/plugin-react

# 2. Remove CRA dependencies
npm uninstall react-scripts

Create vite.config.js at the project root:

JS
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
})

Move public/index.html to the project root and update it: remove the %PUBLIC_URL% references and add a script tag pointing to src/index.jsx:

HTML
<!-- index.html at project root -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/index.jsx"></script>
  </body>
</html>

Update the scripts in package.json:

JSON
{
  "scripts": {
    "start": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

Rename .js files that contain JSX to .jsx (or configure Vite to handle .js files with JSX). Update any process.env.REACT_APP_* environment variables to import.meta.env.VITE_*.

Test after migration
After migrating, run `npm start` (now calling Vite) and check your browser console for any import errors. The most common issues are: files that use JSX still named `.js`, and environment variable names that need the `VITE_` prefix.
When CRA Still Makes Sense
  • You are working on an existing CRA project and a migration is not currently justified

  • A bootcamp or course curriculum requires CRA — follow along and learn the concepts; the React knowledge transfers directly to Vite

  • You need to replicate a tutorial or reproduce a bug report that was written using CRA