Deployment and Build Optimization in React
Deployment and Build Optimization
~15 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
So far our project has only run via npm run dev – Vite's development server with hot reload, but NOT meant for real users. This chapter covers the final step: creating an optimized production build and understanding what happens along the way.
Running npm run build
npm run buildThis produces a dist/ folder with static HTML, CSS, and JavaScript files – NO Node.js server needed to serve them (our own reviews server/ from "React for Beginners" chapters 25/26 is a separate, STANDALONE Node application and stays unaffected by this, needs to be deployed SEPARATELY).
What esbuild/Rollup does under the hood
- Minification: variable names get shortened, whitespace/comments removed – smaller file size, unreadable to humans, functionally identical to the browser.
- Tree shaking: UNUSED code (e.g. functions from
react-windowwe never imported) gets removed entirely from the final bundle. - Code splitting: Remember
lazy()from "React for Beginners" chapter 20? EVERYlazy()-imported page (ReviewsPage,LoginPage,AccountPage,CartPage) becomes its OWN, separately loadable file – users only download the code for the route they actually visit. - Asset hashing: filenames get a content hash (
index-a3f8b2.jsinstead ofindex.js) – if the content changes, the hash changes, allowing browser caching to be used safely and aggressively (the old version stays valid in the cache as long as the filename/hash doesn't change).
Analyzing the build: what's actually in there?
npm run build shows an overview of the generated files with their sizes at the end. For a DEEPER analysis (which PACKAGE contributes how much), there's the rollup-plugin-visualizer plugin:
npm install --save-dev rollup-plugin-visualizer// In vite.config.js:
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [react(), visualizer({ open: true })],
// ... rest of the configuration
});After npm run build, an interactive graphic (a "treemap") opens automatically – bigger boxes are bigger packages in the bundle. A common finding in real projects: ONE accidentally imported, huge library (e.g. all of lodash instead of a single function from it) dominates the entire graphic.
Environment variables for different environments
Our src/api/reviewsApi.js and server/db.js have fixed URLs/credentials hardcoded (http://localhost:4000, secretpassword) – convenient for local development in this tutorial, but NOT suitable for a real production build. Vite supports .env files natively:
VITE_API_BASE_URL=https://api.my-product-catalog.com// In src/api/reviewsApi.js, instead of the hardcoded URL:
const BASE_URL = import.meta.env.VITE_API_BASE_URL;Achtung: ONLY variables prefixed with VITE_ get embedded into the client code by Vite – that's DELIBERATE, to prevent server secrets (database passwords, API keys) from ACCIDENTALLY ending up in the publicly served JavaScript bundle. The secretpassword in server/db.js should NEVER go into a VITE_ variable – server/ runs as a standalone Node process and needs NO Vite environment variable system, plain process.env variables on the server are enough.
Where to host the dist folder?
The dist/ folder is pure static HTML/CSS/JS – hostable on ANY static hosting service (Netlify, Vercel, Cloudflare Pages, GitHub Pages, a plain Nginx web server). The reviews server/, on the other hand, needs REAL Node.js hosting (Railway, Render, your own VPS) – two DIFFERENT deployment targets for the two parts of our project.
Tipp: Rule of thumb for build size: medium-sized React projects often land at 150-300 KB (gzipped) for the initial JavaScript bundle. Noticeably larger? The visualizer from this chapter is the first step toward diagnosing why – often it's unused dependencies or missing code splitting (see lazy()), not "React itself is too big".