7.4 · difficulty 3/4 · 14 min read
Code Splitting and Chunk Strategy
Rolldown's code splitting mechanism — automatic splitting via dynamic import, manual control with manualChunks, and chunk naming strategies. How to optimize output size and loading performance.
Vite 8.1Stable
Automatic splitting with dynamic import
Any dynamic import() automatically triggers code splitting:
ts
// Route-based lazy loading (using React Router as an example)
const Home = lazy(() => import("./pages/Home"))
const About = lazy(() => import("./pages/About"))
const Admin = lazy(() => import("./pages/Admin"))Build output:
text
dist/
assets/
index-HASH.js ← entry chunk (contains route config)
Home-HASH.js ← lazily loaded page chunk
About-HASH.js
Admin-HASH.js
vendor-HASH.js ← shared dependencies (react, etc.)When a user visits the home page, only index.js and Home.js are downloaded. Other pages load on demand.
manualChunks: manual splitting control
Object format (simple)
ts
export default defineConfig({
build: {
rolldownOptions: {
output: {
manualChunks: {
// chunk name → modules to include (package names or file paths)
"react-vendor": ["react", "react-dom", "react-router-dom"],
"ui-vendor": ["@radix-ui/react-dialog", "@base-ui/react"],
utils: ["lodash-es", "date-fns", "axios"],
},
},
},
},
})Function format (flexible)
ts
manualChunks(id: string) {
// id is the module's file path
if (id.includes("node_modules")) {
// Split all node_modules by package name
const match = id.match(/node_modules\/([^/]+)\//)
const packageName = match?.[1]
if (!packageName) return undefined
// Group the React ecosystem together
if (packageName.startsWith("react") || packageName === "scheduler") {
return "react-vendor"
}
// Radix UI component library
if (packageName.startsWith("@radix-ui")) {
return "radix-vendor"
}
// Utility libraries
if (["lodash-es", "date-fns", "dayjs"].includes(packageName)) {
return "utils"
}
// Merge smaller packages into vendor
return "vendor"
}
// Split page code by route
if (id.includes("/pages/")) {
const pageName = id.match(/\/pages\/([^/]+)\//)?.[1]
if (pageName) return `page-${pageName}`
}
},Chunk naming strategy
ts
export default defineConfig({
build: {
rolldownOptions: {
output: {
// File name for dynamic chunks (lazily loaded)
chunkFileNames: (chunkInfo) => {
const { name } = chunkInfo
if (name.startsWith("page-")) {
return "pages/[name]-[hash].js"
}
return "assets/[name]-[hash].js"
},
// File name for entry chunks
entryFileNames: "assets/[name]-[hash].js",
// File name for static assets (images, fonts, etc.)
assetFileNames: "assets/[name]-[hash][extname]",
},
},
},
})Analyzing your bundle
Use rollup-plugin-visualizer to analyze your build output:
bash
pnpm add -D rollup-plugin-visualizerts
import { defineConfig } from "vite"
import { visualizer } from "rollup-plugin-visualizer"
export default defineConfig({
plugins: [
visualizer({
open: true, // open automatically after build
gzipSize: true, // show gzip size
brotliSize: true, // show brotli size
filename: "stats.html",
}),
],
})Run pnpm build and a visualization chart opens automatically — you can see at a glance which packages take up the most space.
Preload strategy
Vite automatically generates preload hints to reduce latency when lazy loading:
html
<!-- Vite automatically injects modulepreload into HTML -->
<link rel="modulepreload" href="/assets/react-vendor-HASH.js" />
<link rel="modulepreload" href="/assets/index-HASH.js" />You can disable this:
ts
export default defineConfig({
build: {
modulePreload: false, // disable preloading
// or
modulePreload: {
polyfill: false, // don't inject the modulepreload polyfill
},
},
})Self-check
- What is the difference between dynamic
import()and staticimport? How does each affect the bundle output? - What are the respective advantages and disadvantages of the object format vs. the function format of
manualChunks? - If two pages both import the same large component, will Rolldown automatically create a shared chunk?
- What practical purpose does the
chunkFileNamesconfiguration serve?
ts
// You have an e-commerce application with the following modules:
// - react, react-dom (framework)
// - antd (UI library, large)
// - lodash-es (utility library)
// - 4 pages: Home / ProductList / ProductDetail / Cart
// - 1 admin panel entry point: /admin
// Design a reasonable code splitting strategy:
// 1. What should go into the vendor chunk?
// 2. How should pages be split?
// 3. How should the admin panel be handled?
// Write the manualChunks configuration:
const manualChunks = (id: string) => {
// TODO
}