Build Tools (Vite)
Modern React apps almost never ship raw files to the browser. Your JSX, TypeScript, CSS modules, and image imports all have to be transformed and bundled first, and during development you want that work to happen in milliseconds so the page updates the instant you hit save. The tool that does this is your build tool, and for new React projects in 2026 the default answer is Vite. This page covers what Vite does, the engines that power it, how to wire up environment variables, and why the once-ubiquitous Create React App is now deprecated.
Why you need a build tool
Browsers do not understand JSX, and shipping hundreds of unbundled ES modules over the network is slow. A build tool solves three problems at once: it transforms non-standard syntax (JSX, TypeScript) into plain JavaScript, it bundles and optimizes your code for production, and it gives you a dev server with Hot Module Replacement (HMR) so edits appear without a full page reload. Vite does all three, and does the dev part exceptionally fast.
Getting started with Vite
The fastest way to scaffold a project is the official create-vite initializer. It generates a ready-to-run React app with sensible defaults.
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev
For TypeScript, swap the template to react-ts. After npm run dev, Vite prints the local URL:
Output:
VITE v6.0.0 ready in 312 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
➜ press h + enter to show help
The generated package.json exposes three scripts you will use constantly:
| Script | Command | Purpose |
|---|---|---|
dev | vite | Start the dev server with HMR |
build | vite build | Produce an optimized production bundle in dist/ |
preview | vite preview | Serve the built dist/ locally to sanity-check production output |
How Vite works under the hood
Vite’s speed comes from using two different engines for two different jobs.
In development, Vite serves your source files over native ES modules. There is no upfront bundling step — the browser requests each module, and Vite transforms it on demand. Those transforms (JSX and TypeScript stripping) are handled by esbuild, a bundler written in Go that is one to two orders of magnitude faster than JavaScript-based tools. This is why the dev server starts in a few hundred milliseconds regardless of project size.
For production, Vite switches to Rollup, which performs whole-program optimization: tree-shaking to drop dead code, code-splitting into chunks, asset hashing for cache-busting, and minification. You get fast startup in dev and a tightly optimized bundle in production.
Vite pre-bundles your
node_modulesdependencies with esbuild on first run and caches the result innode_modules/.vite. If a dependency misbehaves after an upgrade, delete that cache (or runvite --force) to rebuild it.
The React plugin (@vitejs/plugin-react) ties it together. A minimal config looks like this:
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: { port: 3000 },
});
Hot Module Replacement
HMR replaces edited modules in the running app without reloading the page, preserving component state. The React plugin enables Fast Refresh, so editing a component updates it in place. You generally get HMR for free, but if you write a module that needs custom handling you can opt in via the import.meta.hot API:
export function counter() {
let value = 0;
return () => (value += 1);
}
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
console.log('Module updated without a full reload', newModule);
});
}
Environment variables
Vite exposes env vars on import.meta.env rather than process.env. To prevent leaking secrets into the client bundle, only variables prefixed with VITE_ are exposed to your code. Define them in .env files at the project root:
# .env
VITE_API_URL=https://api.example.com
SECRET_KEY=never-exposed
function App() {
// Available in the browser because of the VITE_ prefix.
const apiUrl = import.meta.env.VITE_API_URL;
// import.meta.env.SECRET_KEY is undefined in the client — by design.
return <p>Talking to {apiUrl}</p>;
}
Vite also provides built-in flags: import.meta.env.MODE, import.meta.env.DEV, and import.meta.env.PROD. Use .env.development and .env.production for mode-specific values, and never put true secrets in any VITE_-prefixed variable.
Why Create React App is deprecated
Create React App (CRA) was the standard scaffolder for years, but the React team officially deprecated it in February 2025 and the React documentation no longer recommends it. CRA was built on webpack with a slow, full-bundle dev server; cold starts and rebuilds grew painful as apps scaled. It also fell behind on maintenance, shipping outdated dependencies and leaving security audit warnings unaddressed.
| Create React App | Vite | |
|---|---|---|
| Dev engine | webpack (bundle everything) | esbuild + native ESM |
| Cold start | Seconds, grows with app size | ~Sub-second, near-constant |
| Production bundler | webpack | Rollup |
| Status | Deprecated (2025) | Actively maintained default |
The official migration path is to either move to a full framework like Next.js or React Router, or, for a plain single-page app, switch to Vite. Migration is usually quick: install vite and @vitejs/plugin-react, move index.html to the project root with a <script type="module" src="/src/main.jsx"> entry, and rename process.env.REACT_APP_* variables to import.meta.env.VITE_*.
Best Practices
- Scaffold new SPAs with
create-vite; reach for Next.js or React Router when you need routing, SSR, or data loading conventions. - Prefix any client-readable env var with
VITE_, and keep real secrets server-side — never in the client bundle. - Run
npm run previewbefore deploying to test the actual production build, not just the dev server. - Add
vite --forceto your troubleshooting toolkit when a dependency upgrade leaves a stale pre-bundle cache. - Keep
vite.config.jslean; reach for plugins only when you need them, since each one runs on every transform. - Migrate off Create React App — it is deprecated and unmaintained.