Code Splitting
- React
- Next.js
Breaking an app’s JavaScript bundle into smaller chunks that load on demand, so users download only the code the current view needs.
Prerequisites
- Lazy loading (see Related Topics)
What you'll understand
- What a bundle is and why its size matters
- How code splitting breaks a bundle into chunks
- How splitting relates to lazy loading and routing
Explanation
When you build a frontend, your code and its dependencies are packaged into a bundle of JavaScript the browser downloads and runs. As an app grows, a single bundle grows with it, and the user ends up downloading the code for every feature, including ones they never open, just to see the first screen. Large bundles mean slower initial loads.
Code splitting breaks that one big bundle into smaller chunks that can be loaded independently. Rather than shipping everything at once, the build creates separate pieces, and the browser fetches a chunk only when it is needed. The trigger is usually a dynamic import: importing a module with import() tells the bundler to split it into its own chunk that loads on demand.
Code splitting and lazy loading are two sides of the same coin (see Related Topics): splitting produces the chunks, and lazy loading is how you fetch one when required. The most natural place to split is along routes, so each page’s code loads only when a user navigates to it (see Related Topics). Frameworks help here, Next.js splits by route automatically, so you get much of the benefit without manual work.
Examples
A dynamic import tells the bundler to put a module in its own chunk, loaded on demand:
// Instead of a static top-level import, load on demand:
const chart = await import('./HeavyChart');
// The bundler places HeavyChart in a separate chunk.With React, the same idea drives component-level splitting via lazy (see Related Topics):
const HeavyChart = lazy(() => import('./HeavyChart'));Common mistakes
- Shipping one enormous bundle and blaming the framework for slow loads.
- Splitting so aggressively that the app makes many tiny requests.
- Splitting code that is needed immediately, adding a load delay for no gain.
- Ignoring what the bundler already splits automatically and duplicating the effort.
Best practices
- Split along routes so each view loads only its own code.
- Use dynamic imports to separate heavy, rarely used modules.
- Pair splitting with lazy loading and a loading fallback (see Related Topics).
- Measure bundle size and let the framework handle routine splitting.
Further reading
- React, lazy — https://react.dev/reference/react/lazy
- Next.js, Lazy loading — https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading