TL;DR — Quick Summary
Code splitting divides JavaScript into smaller chunks loaded on demand rather than all upfront. Use route-based splitting (automatic in frameworks), React.lazy() for component-level splitting, and dynamic import() for feature-level splitting. Target ≤ 200KB initial bundle.
What is Code Splitting?
Code splitting breaks a JavaScript application into multiple smaller bundles (called 'chunks') that are loaded on demand rather than all at once. The browser downloads only the JavaScript needed for the current view, deferring the rest until it's needed.
Implementation levels:
- •Route-based splitting — Each page/route gets its own chunk. When the user navigates to /dashboard, only the dashboard code loads. This is the most impactful level and is automatic in frameworks like Next.js, Remix, and Gatsby.
- •Component-level splitting — Individual components are loaded on demand using React.lazy() + Suspense. Ideal for heavy components like charts, editors, modals, and admin panels.
- •Feature-level splitting — Specific features or libraries are loaded when triggered. For example, loading a PDF library only when the user clicks 'Export PDF.'
All three levels use the dynamic `import()` syntax under the hood. The bundler analyzes import() calls at build time and creates separate chunk files.
History & Evolution
Key milestones:
- •2014 — Webpack introduces code splitting via require.ensure().
- •2015 — Dynamic import() proposal begins TC39 process.
- •2017 — React 16.6 introduces React.lazy() and Suspense for component-level code splitting.
- •2018 — Dynamic import() becomes standard JavaScript. Webpack, Rollup, and Parcel all support it.
- •2020 — Next.js popularizes automatic route-based splitting — developers get code splitting 'for free.'
- •2023 — React Server Components introduce a new paradigm: server components don't ship JavaScript to the client at all.
- •2025–2026 — Vite (Rollup) is the dominant bundler with automatic code splitting. Framework-level splitting is the norm.
How Code Splitting is Measured
Code splitting effectiveness is measured by comparing bundle sizes before and after:
Tools for analysis:
- •Bundle analyzers — rollup-plugin-visualizer (Vite), webpack-bundle-analyzer (Webpack) show visual treemaps of bundle contents and chunk sizes.
- •Chrome DevTools Coverage tab — Shows what percentage of loaded JavaScript is actually used. High unused % indicates splitting opportunities.
- •Lighthouse — 'Reduce unused JavaScript' audit identifies code that could be split or deferred.
Key rule: Field data (CrUX) determines Google rankings. Lab data (Lighthouse, WebPageTest) is for debugging and iteration.
Common Causes of Poor Code Splitting Scores
Common issues that indicate missing or poor code splitting:
- 1Single large bundle — All application code in one file (vendor.js or main.js > 500KB).
- 2All routes loaded upfront — SPA loads dashboard, settings, admin code on the homepage.
- 3Heavy libraries loaded globally — Chart.js, Monaco Editor, PDF.js imported in the main bundle.
- 4Barrel file imports — `import { Button } from '@/components'` may pull in every component.
- 5No framework-level splitting — Custom React Router setup without lazy routes.
Frequently Asked Questions
Code splitting breaks a single large JavaScript bundle into multiple smaller chunks that load on demand. Instead of downloading all application code upfront, the browser only loads what's needed for the current view.
Use React.lazy() + Suspense: `const Page = React.lazy(() => import('./Page'))`. Wrap in `<Suspense fallback={<Loading />}><Page /></Suspense>`. For routing, wrap each route component in React.lazy().
Route-based splitting creates a separate chunk per page/route — most impactful and should be done first. Component-level splitting defers individual heavy components (charts, editors) — use for components > 50KB.
Not directly — search engines can execute JavaScript. However, code splitting improves CWV (LCP, TBT), which are ranking factors. SSR/SSG ensures content is in the initial HTML regardless of JS splitting.
Target ≤ 200KB of JavaScript for the initial page load (after minification, before compression). Each route chunk should be ≤ 200–300KB. Use bundle analyzers to verify.
Yes — use `<link rel='prefetch'>` for likely next-route chunks, or Speculation Rules for more sophisticated prefetch. This makes subsequent navigations feel instant despite code splitting.
They're complementary. Tree shaking removes unused exports from each chunk. Code splitting separates chunks by usage pattern. Apply both: tree shake to minimize what's in each chunk, code split to defer chunks not needed immediately.
Bundlers automatically extract shared dependencies into a 'common' or 'vendor' chunk. If the common chunk grows too large (> 200KB), consider splitting it further or using dynamic imports for large shared libraries.
For step-by-step optimization, platform-specific fixes, code examples, and case studies, read our full guide:
The Ultimate Guide to Core Web Vitals: How to Pass All Metrics & Boost Rankings in 2026Struggling with Code Splitting?
Request a free speed audit and we'll identify exactly what's holding your scores back.