Running npm run build switches Vite to an entirely different pipeline than development: Rollup produces a static, optimized dist/ folder ready for deployment. This lesson covers what lands in that output, automatic code splitting, previewing the real build locally, and configuring subpath deployments.
1From Dev Server to Shippable Bundle
The native-ESM, HMR-driven experience of npm run dev is entirely separate from what happens during npm run build. The build command switches to Rollup, which bundles, minifies, and optimizes the app into a small set of static files ready to deploy.
2What Lands in dist/
The output is a self-contained, static dist/ folder: a processed index.html, JavaScript bundles with content-hashed names, extracted and minified CSS, and static assets. Because it has no server-side runtime dependency, it can be hosted on any static file host, CDN, or object storage service.
3Automatic Code Splitting via Dynamic Import
Rollup automatically produces a separate chunk for any module only reached through a dynamic import(), such as one used with React.lazy for route-based code splitting. That chunk is downloaded only when actually needed, rather than being included in the initial page load.
4Previewing the Build Locally
Since npm run dev never runs the actual production pipeline, vite preview exists specifically to serve the real dist/ output locally, letting developers verify production-only behavior ā final chunk splitting, minification ā before deploying to a live environment.
5Configuring the base Path
Apps deployed under a subpath rather than a domain root need the base option set in vite.config.ts so every generated asset URL is correctly prefixed. A mismatched base is one of the most common causes of a blank page after deploying to a subpath.
6Step-by-Step Breakdown
From Dev Server to Shippable Bundle. Everything you've learned so far ā native ESM serving, HMR, on-demand asset processing ā describes the development experience. Running npm run build triggers an entirely different pipeline: Rollup bundles, minifies, and optimizes your app into a small set of static files ready to deploy anywhere.
What Lands in dist/. The build output is a static dist/ folder: a processed index.html, JavaScript bundles with content-hashed filenames, extracted and minified CSS, and any static assets. This output has no server-side dependency ā it can be hosted on any static file host, CDN, or object storage bucket.
What kind of hosting does a standard Vite React app's dist/ output require?
- āAny static file host or CDN ā no server-side runtime needed
- āA dedicated Node.js server running the build output
Automatic Code Splitting via Dynamic Import. Rollup automatically creates a separate chunk for every module reached only through a dynamic import(). If you lazily load a route with React.lazy(() => import('./Settings')), Rollup emits Settings as its own file, downloaded only when a user actually navigates there ā not bundled into the initial page load.
Previewing the Build Locally. npm run dev never shows you the real production bundle. vite preview starts a lightweight local server that serves your actual dist/ output, so you can verify the built app behaves correctly ā including things that only exist in production, like final chunk splitting and minified code ā before deploying it.
Why should you run vite preview before deploying, instead of only relying on npm run dev?
- āIt serves the actual, bundled dist/ output, not the dev-mode native ESM version
- āIt's the only way to see console.log output
Configuring the base Path. If your app is deployed at a subpath instead of the domain root ā like example.com/my-app/ rather than example.com/ ā set base in vite.config.ts so every generated asset URL is correctly prefixed. Getting this wrong is one of the most common causes of a blank white screen after deployment.
Mastery Achieved. You now understand the Vite production build: a fully static dist/ output with content-hashed filenames, automatic code splitting from dynamic imports, vite preview for verifying real production behavior locally, and the base config for subpath deployments. Next, you'll dig into fine-tuning that build for size and speed.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Production build output targets modern browsers by default (build.target).
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Verify Accessibility Against the Real Production Build
Minification and chunk splitting can occasionally interact with third-party accessibility tooling differently than the dev build ā run screen reader and keyboard-navigation checks against vite preview output, not just the dev server, before a release.
SEO Implications
- 1
A Correct base Path Is Essential for Crawlability
If asset URLs resolve incorrectly due to a misconfigured base path, the deployed page can fail to load its JavaScript entirely, which for client-rendered content means crawlers and users alike see a broken, empty page.
Best Practices
Always Run vite preview Before Deploying a Significant Change
Development mode hides production-only issues like chunk splitting boundaries and minification edge cases ā a quick local preview catches these before they reach real users.
Lazy-Load Routes and Heavy, Rarely-Used Components
Wrapping route-level components (and large, infrequently used ones like a rich text editor) in React.lazy lets Rollup split them into separate chunks, keeping the initial bundle small.
Frequent Bugs
The app shows a blank white screen after deploying to a subpath like example.com/my-app/.
The base option in vite.config.ts wasn't set (or was set incorrectly) to match the deployed subpath, so generated asset URLs point to the wrong location. Set base: '/my-app/' to match exactly.
A feature works fine locally in dev mode but breaks in the deployed production build.
Development uses unbundled native ESM; production uses a minified Rollup bundle. Reproduce the issue locally with npm run build && npm run preview to debug against the actual production code path.
Real-World Examples
Deploying a Vite App to a GitHub Pages Subpath
A team deployed their Vite React app to GitHub Pages at username.github.io/project-name/, a subpath rather than the domain root. Without setting base: '/project-name/' in vite.config.ts, all asset requests resolved against the wrong root and the page loaded blank; setting the correct base fixed every asset URL.
// vite.config.ts
export default defineConfig({
base: '/project-name/',
plugins: [react()],
});