Back to React
React
easy
mid

How would you create a React app without using create react app?

Yes — CRA is deprecated. Modern starters use Vite (`npm create vite@latest`) or Next.js (`npx create-next-app`). For full control, install `react`, `react-dom`, a bundler (Vite/esbuild/Rspack), TypeScript, and an entry that calls `createRoot(...).render(<App/>)`. CRA is slow and unmaintained — there is no reason to start with it in 2025+.

6 min read·~12 min to think through

Yes — and you should. CRA was officially deprecated by the React team in early 2025. Vite or Next.js is the default starting point now.

Option 1: Vite (recommended for SPAs)

bash
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

You get: TypeScript, HMR under 100ms, esbuild for transpile, Rollup for production builds. The dev server is ~10× faster than CRA's webpack.

Option 2: Next.js (recommended for production apps)

bash
npx create-next-app@latest my-app --typescript --app

You get routing, SSR/SSG, image optimization, API routes, and a deployment target (Vercel/self-host). Default for anything user-facing.

Option 3: From scratch

Helpful to understand what the starters give you:

bash
npm init -y
npm i react react-dom
npm i -D vite @vitejs/plugin-react typescript @types/react @types/react-dom

index.html:

html
<!doctype html>
<html>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

src/main.tsx:

tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

vite.config.ts:

ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()] });

Why CRA died

  • Webpack dev server didn't keep up; cold start on medium apps was 30+ seconds.
  • The 'eject' escape hatch was one-way and scary.
  • No support for React Server Components, modern routing, or any of the things production apps actually need.
  • Last meaningful release was 2022.

The React team's own docs now point new users to Vite or a framework like Next.js / Remix.

Follow-up questions

  • What does Vite do differently from webpack in dev?
  • When would you pick Vite over Next.js?
  • How do you set up TypeScript path aliases in Vite?

Common mistakes

  • Following an old tutorial that says 'run create-react-app' — it's deprecated.
  • Picking Next.js for a tiny SPA and dealing with SSR you don't need.
  • Skipping StrictMode in the root, then debugging effects in production.

Performance considerations

  • Vite's dev server is ESM-on-demand; only files you import get transformed. Production builds use Rollup with tree-shaking and code-splitting by default. Next.js adds automatic route-level code splitting, image optimization, and font subsetting.

Edge cases

  • Vite uses ESM in dev — some old CJS libs need `optimizeDeps.include`.
  • Next.js App Router and Pages Router are different mental models — don't mix without intent.
  • If you ship to a non-Vercel host, Next.js needs a Node runtime or an adapter.

Real-world examples

  • Most modern React projects you'll see in interviews use Vite (small/medium SPAs) or Next.js (anything with SEO, auth, or a backend). CRA-bootstrapped projects are usually 3+ years old and slated for migration.

Senior engineer discussion

The interesting question isn't 'can you avoid CRA' but 'what does a bundler need to give you'. JSX transform, fast HMR, code splitting, asset hashing, CSS handling, tree-shaking, source maps. Knowing these means you can evaluate any new starter on its merits.

Related questions