---
title: "03 — Vite and the React Tooling Story — Why Nobody Uses create-react-app Anymore"
uid: cli-and-tooling
tags: ["build-tools", "react", "tooling", "vite", "roadmap:react"]
excerpt: "Vite replaced create-react-app because it's built on native ES modules in dev and esbuild/Rollup for the bundle — near-instant cold starts and hot reload that actually feels hot."
date: 2026-08-13T03:27:46+0000
source: https://www.aveshina.my.id/en/blog/cli-and-tooling
---

"The tooling just works" was my React-tooling stance, which made the CRA-to-Vite shift look like fashion. The shift worth understanding: **Vite replaced create-react-app as the default React starter because it's built around native ES modules in dev and esbuild/Rollup for the bundle, which means near-instant cold starts and hot-reload that actually feels hot.** [1][2] The CRA era died because its dev server re-bundled the entire app on every change; Vite doesn't.

## What a React app actually needs from its tooling

Before picking a tool, I had to see what the tool is doing. A modern React app needs four things out of the box:

- **A dev server** that serves the app over HTTP during development and rebuilds on save.
- **Hot Module Replacement (HMR)** — swap just the changed module into the running page without a full reload, preserving component state.
- **A JSX/TypeScript compiler** — browsers don't speak JSX or TS, so the tool converts (transpiles) it on the fly.
- **A production bundler** — for the final build, tree-shake (drop the code you don't use) and minify (shrink) everything into static assets.

create-react-app bundled all four behind a react-scripts black box, and it did it by re-running Webpack over the whole dependency graph whenever anything changed. On a large app that meant a 30-second cold start and multi-second reloads. That slowness is what killed it.

## Why Vite is fast

Vite's core trick is the split between dev and build [1][2]:

- **In dev**, Vite serves the app unbundled. The browser loads modules natively (<script type="module">), and Vite only transpiles the module the browser asks for — on demand, one file at a time, using esbuild (which is orders of magnitude faster than Babel). Cold start time scales with the page you're viewing, not the size of node_modules.
- **For the production build**, Vite uses Rollup to produce the optimized bundle — tree-shaken, code-split, minified.

The practical effect: npm run dev is up before I've finished alt-tabbing to the browser, and edits show up in the page within milliseconds. That speed isn't a luxury; it changes how it feels to iterate.

```figure
<svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Two pipelines compared. Left, CRA era: every save re-bundles the entire dependency graph through Webpack before the browser sees a change — slow. Right, Vite: the browser requests ES modules on demand, and esbuild transpiles only the file asked for, so HMR is near-instant.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <!-- header -->
    <text x="180" y="26" font-size="12" font-weight="700" fill="#7f1d1d" text-anchor="middle">CRA / Webpack era</text>
    <text x="540" y="26" font-size="12" font-weight="700" fill="#1e1b4b" text-anchor="middle">Vite</text>

    <!-- LEFT pipeline -->
    <rect x="40" y="46" width="120" height="44" rx="8" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="100" y="73" font-size="11" font-weight="700" fill="#7f1d1d" text-anchor="middle">save</text>

    <rect x="40" y="110" width="280" height="44" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="180" y="130" font-size="11" font-weight="700" fill="#422006" text-anchor="middle">re-bundle WHOLE graph</text>
    <text x="180" y="145" font-size="9.5" fill="#422006" text-anchor="middle">even node_modules · slow</text>

    <rect x="40" y="174" width="120" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="100" y="201" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">browser</text>

    <path d="M100,90 L100,108" stroke="#64748b" stroke-width="1.5" marker-end="url(#none)"/>
    <path d="M100,154 L100,172" stroke="#64748b" stroke-width="1.5"/>

    <!-- RIGHT pipeline -->
    <rect x="400" y="46" width="120" height="44" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="460" y="73" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">save</text>

    <rect x="400" y="110" width="280" height="44" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="540" y="130" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">esbuild transpiles ONE file</text>
    <text x="540" y="145" font-size="9.5" fill="#1e1b4b" text-anchor="middle">browser requests it on demand</text>

    <rect x="400" y="174" width="120" height="44" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="460" y="201" font-size="11" font-weight="700" fill="#052e16" text-anchor="middle">browser</text>

    <path d="M460,90 L460,108" stroke="#64748b" stroke-width="1.5"/>
    <path d="M460,154 L460,172" stroke="#64748b" stroke-width="1.5"/>

    <!-- captions -->
    <text x="180" y="246" font-size="10.5" fill="#7f1d1d" text-anchor="middle" font-style="italic">cold start scales with node_modules</text>
    <text x="540" y="246" font-size="10.5" fill="#1e1b4b" text-anchor="middle" font-style="italic">cold start scales with one page</text>
    <text x="360" y="270" font-size="10" fill="#9ca3af" text-anchor="middle" font-style="italic">native ES modules in dev · Rollup bundle for production</text>
  </g>
</svg>
```

## Starting a project today

The roadmap's CLI section points at Vite as the default [1][3], and the command is one line. Vite ships official React templates for both JavaScript and TypeScript:

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

That's the whole onboarding. There's no react-scripts eject cliff — the build config is right there in vite.config.ts, and I can extend it with plugins (React Fast Refresh, path aliases, environment variables) without fighting an opaque wrapper.

One caveat the roadmap implicitly assumes: if I'm building a full app with routing, SSR (server-side rendering), and API routes, Vite alone isn't the whole answer — that's where Next.js or Remix (now folded into React Router v7) come in. Vite is the build tool underneath many of them, and it's the right default for a client-rendered SPA. The notes on SSR frameworks cover that layer.

## How I use this

For any new client-rendered React project, I reach for npm create vite with the react-ts template, full stop. I haven't touched create-react-app in years, and I'd treat its presence in a codebase as a signal to migrate. When the project outgrows an SPA — needs server rendering, file-based routes, a backend — that's the moment to move to Next.js or a Vite-based meta-framework, not to bolt complexity onto a bare Vite setup. And because Vite is the engine under so many of those meta-frameworks, learning its config model pays off everywhere downstream.

## References

[1] Vite team, "Vite — Next Generation Frontend Tooling," vitejs.dev, 2024. [Online]. Available: [https://vitejs.dev](https://vitejs.dev)

[2] Vite team, "Why Vite," Vite Docs, 2024. [Online]. Available: [https://vitejs.dev/guide/why.html](https://vitejs.dev/guide/why.html)

[3] Vite team, "Getting Started with Vite," Vite Docs, 2024. [Online]. Available: [https://vitejs.dev/guide/](https://vitejs.dev/guide/)

[4] E. You, "Vite Crash Course," YouTube, 2023. [Video]. Available: [https://youtu.be/LQQ3CR2JTX8](https://youtu.be/LQQ3CR2JTX8)

[5] T. Vejraka, "Vite Crash Course — Faster Alternative To CRA," YouTube, 2023. [Video]. Available: [https://www.youtube.com/watch?v=89NJdbYTgJ8](https://www.youtube.com/watch?v=89NJdbYTgJ8)

```quiz
Q: Why did create-react-app fall out of favor?
- Its Webpack-based dev server re-bundled the whole dependency graph on every change, making it slow
- It only supported class components
- It could not compile JSX
correct: 0
explain: CRA wrapped Webpack and re-bundled the entire graph (including node_modules) on each change. Cold starts and reloads grew painful on large apps, which is what Vite's on-demand model fixed.

Q: In Vite's dev server, how is code served to the browser?
- As one pre-bundled chunk rebuilt on every save
- As native ES modules, transpiled one file at a time by esbuild on demand
- As raw TypeScript the browser interprets directly
correct: 1
explain: Vite serves modules unbundled via native ESM; esbuild transpiles only the file the browser requests. Cold start scales with the active page, not with node_modules.

Q: What does Vite use for the production bundle?
- Webpack
- esbuild
- Rollup
correct: 2
explain: Dev uses esbuild for fast per-file transpilation; the optimized production build is produced with Rollup (tree-shaking, code-splitting, minification).

Q: For a new client-rendered React SPA in 2024+, the default starter is…
- create-react-app
- npm create vite with the react or react-ts template
- a hand-rolled webpack config
correct: 1
explain: Vite is the modern default. CRA is unmaintained and slow; meta-frameworks like Next.js take over once SSR or API routes are needed.
```
