Skip to content

React Tutorial Modern React and Production

Production Builds with Vite: What npm run build Does

Lesson 132 of 156 4 min read By Deepak Paswan Updated Oct 10, 2026

Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.

npm run dev is for working. npm run build creates the files you put on a server. They are different programs with different goals, so always test the build before you ship.

Run it

terminal
BASH
npm run build

For the standard Vite React template (Vite 8.3), the real output was:

code
OUTPUT
vite v8.3.4 building client environment for production...
transforming...
✓ 20 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                   0.45 kB │ gzip:  0.29 kB
dist/assets/react-CHdo91hT.svg    4.12 kB │ gzip:  2.06 kB
dist/assets/vite-BF8QNONU.svg     8.70 kB │ gzip:  1.60 kB
dist/assets/hero-CLDdwZDr.png    13.05 kB
dist/assets/index-DykytF2W.css    4.10 kB │ gzip:  1.47 kB
dist/assets/index-D2sGXTia.js   222.44 kB │ gzip: 69.28 kB

✓ built in 218ms

(The hash parts of the file names and the sizes will differ in your project.) The result is a dist folder of plain static files. Nothing in it needs Node.js to run.

What the build does

  • Bundles your many modules (and React) into a few files, so the browser makes fewer requests.
  • Minifies the JavaScript and CSS: shorter names, no spaces or comments, and unused code is removed (tree shaking).
  • Uses the production build of React, without the development warnings and checks. React’s own code is smaller and much faster. That is why you must measure performance on a production build, never on npm run dev.
  • Adds a content hash to file names (index-D2sGXTia.js). When a file changes, its name changes, so browsers and CDNs can cache every asset for a very long time, and still pick up new versions.
  • Splits code wherever you used dynamic import() (Lazy Loading). One verified example produced index-BS2MIXql.js plus a separate HeavyPanel-CKINAcrt.js chunk.
  • Rewrites index.html so it points at the hashed files.
  • Copies public/ into dist unchanged.
  • In a TypeScript project, npm run build first runs tsc -b, so type errors fail the build.

Preview the production build

terminal
BASH
npm run preview
code
OUTPUT
  ➜  Local:   http://localhost:4173/
  ➜  Network: use --host to expose

This serves the dist folder locally. Use it to check the real thing: navigate around, open the Network tab, run a Lighthouse audit. It is a convenient check, not a production server; do not expose it to the internet.

Dev vs production: differences that surprise people

npm run devnpm run build
ReactDevelopment build: extra warnings, slowerProduction build: faster and smaller
Strict Mode double render and effectsYesNo
import.meta.env.DEV / .PRODtrue / falsefalse / true
FilesMany separate modulesA few hashed bundles
Environment valuesRead from .env, .env.developmentRead from .env, .env.production, and inlined
ErrorsOverlay in the browserPlain console errors, or a blank page
PathsRoot-relativeDepend on the base setting

Some bugs appear only in production: a wrong base, a missing environment variable, an import written with the wrong letter case (Windows and macOS ignore case, Linux servers do not). That is why you should preview before you deploy.

Important build settings

In vite.config.js:

script.js
JS
export default defineConfig({
  plugins: [react()],
  base: '/',                 // where the app is served from; see below
  build: {
    outDir: 'dist',          // output folder
    sourcemap: false,        // true makes .map files for debugging
  },
})
  • base is the public path prefix for every asset. Use '/' when the app is at the site root, and '/my-app/' when it lives in a subfolder (for example https://example.com/my-app/). A wrong base is the number one cause of “works locally, blank page when deployed”; see Deployment Fundamentals and Common Production Errors. You can also set it on the command line, for example npx vite build --base=/my-app/.
  • Source maps let browser tools show your original code for production errors. Upload them privately to an error-tracking service rather than serving them publicly, if your code is sensitive.
  • build.chunkSizeWarningLimit controls the warning for large chunks (the default is 500 kB after minification). Treat the warning as a hint to split code, not just something to silence.

Check the size

The size of the JavaScript users download matters most. The template above ships about 69 kB gzipped of JavaScript. Things that make it grow quickly: large libraries (charts, editors, date and icon libraries), importing a whole library instead of the parts you use, and a lot of code in the first page that most visits do not need. How to keep it small:

  • Import only what you use (import { format } from 'date-fns', not a whole utility kit).
  • Lazy-load big features and routes.
  • Compare alternatives before adding a dependency (look up its size).
  • Use a bundle analyzer plugin (several exist; pick one that supports your Vite version) to see what is inside the bundle.
  • Check the gzip number (what is actually sent over the network when the server compresses, which it should).

A verified build step

Our projects were built with npx vite build and then opened in a real browser. The dist/index.html references hashed files, for example:

index.html
HTML
<script type="module" crossorigin src="/assets/index-D2sGXTia.js"></script>

If you deploy into a subfolder without setting base, that /assets/... path will be wrong, and the page will be blank.

Key points

  • npm run build bundles, minifies, hashes and splits your code into the static dist folder; npm run preview serves it locally.
  • Development and production behave differently; measure and test the production build.
  • Set base for subfolder deployments, and watch your bundle size.