Vite Deep Dive — Dev Server, HMR, Plugins, SSR & Build Optimization
Vite is a next-generation build tool that leverages native ES modules for a fast dev server and Rollup for optimized production builds. This deep dive covers how Vite works under the hood, its plugin API, SSR support, library mode, environment variables, CSS handling, and a practical comparison with Webpack.
Learning Path
flowchart LR
A["Build Tools<br/>Webpack Basics"] --> B["Vite<br/>This Tutorial"]
B --> C["Vite Plugins<br/>Custom Development"]
C --> D["SSR & Library Mode<br/>Advanced Vite"]
B --> E["Production Build<br/>Rollup Optimization"]
style B fill:#f90,color:#fff,stroke-width:2px
How Vite’s Dev Server Works
flowchart TD
subgraph "Vite Dev Server"
B["Native ESM<br/>Server"]
C["HMR<br/>WebSocket"]
D["Pre-bundling<br/>esbuild"]
end
E["Browser"] -->|"import /src/App.vue"| B
B -->|"Transform on the fly"| E
C <-->|"Hot update"| E
D -->|"node_modules<br/>dependencies"| B
Vite serves source files as native ES modules. The browser imports them directly — no bundling during development.
npx create-vite@latest my-app --template react
cd my-app
npm run devExpected output:
VITE v6.0.0 ready in 135ms
➜ Local: http://localhost:5173/HMR in Detail
Vite’s HMR uses a WebSocket connection between server and browser:
// vite.config.js — Adding custom HMR handling
export default {
server: {
hmr: {
protocol: 'ws',
host: 'localhost',
port: 24678,
overlay: true
}
}
}Each framework integration implements handleHotUpdate:
if (import.meta.hot) {
import.meta.hot.accept('./module.js', (newModule) => {
console.log('Module updated:', newModule)
})
}Rollup-Based Production Build
npm run buildExpected output:
vite v6.0.0 building for production...
✓ built in 3.2s
dist/index.html 0.48 kB
dist/assets/index-D1x2E3F4.js 142.30 kBConfigure Rollup options:
export default {
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash-es', 'date-fns']
}
}
},
target: 'es2020',
minify: 'esbuild', // or 'terser' for IE11 support
sourcemap: true
}
}Plugins
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import legacy from '@vitejs/plugin-legacy'
export default defineConfig({
plugins: [
react(),
legacy({
targets: ['defaults', 'not IE 11']
})
]
})Custom Plugin Example
function myPlugin() {
return {
name: 'my-plugin',
enforce: 'pre',
transform(code, id) {
if (id.endsWith('.special.txt')) {
return {
code: `export default ${JSON.stringify(code)}`,
map: null
}
}
}
}
}SSR (Server-Side Rendering)
// vite.config.js
export default defineConfig({
build: {
ssr: 'src/entry-server.jsx'
}
})// server.js
import express from 'express'
import { createServer } from 'vite'
const app = express()
const vite = await createServer({
server: { middlewareMode: true },
appType: 'custom'
})
app.use(vite.middlewares)
app.use('*', async (req, res) => {
const { render } = await vite.ssrLoadModule('/src/entry-server.jsx')
const html = await render(req.url)
res.send(html)
})Library Mode
export default defineConfig({
build: {
lib: {
entry: 'src/index.ts',
name: 'MyLib',
formats: ['es', 'cjs', 'umd']
},
rollupOptions: {
external: ['react', 'react-dom']
}
}
})Build creates dist/my-lib.mjs, dist/my-lib.cjs, and dist/my-lib.umd.js.
Environment Variables
// .env
VITE_API_URL=https://api.example.com
// .env.production
VITE_API_URL=https://api.prod.example.com
console.log(import.meta.env.VITE_API_URL)Only VITE_ prefixed variables are exposed. Access import.meta.env.MODE for development or production.
CSS Handling
export default defineConfig({
css: {
modules: {
localsConvention: 'camelCaseOnly'
},
postcss: {
plugins: [require('autoprefixer'), require('tailwindcss')]
},
preprocessorOptions: {
scss: {
additionalData: `@import "./src/styles/variables";`
}
}
}
})Vite vs Webpack
| Feature | Vite | Webpack |
|---|---|---|
| Dev server approach | Native ESM, no bundling | Bundle all modules |
| Cold start | ~100–300ms | ~5–30s |
| HMR | Instant (< 50ms) | 200ms–2s |
| Production bundler | Rollup | webpack |
| Configuration | Minimal, sensible defaults | Extensive, verbose |
| Plugin ecosystem | Growing, Rollup-compatible | Mature, massive |
| Best for | New projects (2024+) | Legacy projects, complex webpack configs |
Common Errors
Cannot find modulein production but works in dev — Dev server uses ESM resolution; production uses Rollup. If you use bare imports without installing the package, dev works but build fails. Always install dependencies.- HMR not working with symlinked packages — Vite’s file watcher doesn’t follow symlinks by default. Use
server.watch.ignored: falseor setresolve.symlinks: false. - Environment variables returning
undefined— OnlyVITE_prefixed variables are exposed. Non-prefixed variables are not available in client code. - SSR build errors with Node.js built-ins — Vite’s SSR build runs in Node.js, not the browser. Use
build.rollupOptions.externalto exclude Node modules. - Legacy browser support issues — Vite targets modern browsers. For IE11 or older Chrome, use
@vitejs/plugin-legacywhich generates fallback chunks. - Asset paths wrong in production — Set
base: '/my-app/'in config if your app is served from a subdirectory. - TypeScript path aliases not resolved in build — Configure
resolve.aliasinvite.config.jsmatching yourtsconfig.jsonpaths. - CSS modules not applying — File must be named
*.module.css(or.scss,.less). Plain.cssfiles don’t activate CSS modules.
Practice Questions
1. Why is Vite’s dev server faster than Webpack’s? Vite serves files as native ES modules without bundling. Only the requested file is transformed. Webpack must bundle and rebuild the entire dependency graph on every change.
2. How do you create a library with Vite?
Use build.lib configuration with entry point, name, and formats. The build produces ESM, CJS, and/or UMD outputs.
3. What’s the difference between import.meta.env.MODE and import.meta.env.PROD?
MODE returns the string 'development' or 'production'. PROD returns a boolean (true in production mode).
4. How does Vite handle CSS imports?
Vite supports .css, .scss, .less, .styl, and CSS modules (.module.css) out of the box. PostCSS is applied automatically if postcss.config.js exists.
5. Challenge: Build a Vite plugin
Create a Vite plugin that: reads all .graphql files in the src/ directory, inlines them as JavaScript template literals, and adds a virtual module virtual:graphql-schema that exports all queries combined.
Mini Project: Multi-Page Vite App
export default defineConfig({
build: {
rollupOptions: {
input: {
main: '/index.html',
admin: '/admin.html'
}
}
}
})npm run buildProduces dist/index.html, dist/admin.html, and shared assets.
FAQ
Related Tutorials
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro