Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to implement code splitting in JavaScript

Code splitting divides your JavaScript bundle into smaller chunks that load on demand, reducing initial page load time. As the creator of CoreUI with over 25 years of JavaScript experience since 2000, I’ve implemented code splitting across large applications to dramatically improve first contentful paint times. The standard approach uses dynamic import() to create split points, letting bundlers like webpack or Vite generate separate chunks automatically. This allows users to download only the code they actually need.

Use dynamic import to split code at logical boundaries.

// Static import - bundled into main chunk
import { heavyFeature } from './heavy-feature'

// Dynamic import - creates a separate chunk
button.addEventListener('click', async () => {
  const { heavyFeature } = await import('./heavy-feature')
  heavyFeature()
})

Static imports always end up in the main bundle. Dynamic import() returns a Promise and tells the bundler to split here. The chunk downloads only when this code executes. The initial bundle stays small.

Route-Based Code Splitting

Load route components lazily in React.

import { lazy, Suspense } from 'react'
import { Routes, Route } from 'react-router-dom'

const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
const Reports = lazy(() => import('./pages/Reports'))

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path='/' element={<Dashboard />} />
        <Route path='/settings' element={<Settings />} />
        <Route path='/reports' element={<Reports />} />
      </Routes>
    </Suspense>
  )
}

React.lazy wraps dynamic imports for components. Suspense shows a fallback while loading. Each route becomes a separate chunk. Users downloading the dashboard don’t download reports code.

Component-Level Code Splitting

Split large components loaded conditionally.

import { lazy, Suspense, useState } from 'react'

const HeavyChart = lazy(() => import('./HeavyChart'))
const DataGrid = lazy(() => import('./DataGrid'))

function Dashboard() {
  const [showChart, setShowChart] = useState(false)
  const [showGrid, setShowGrid] = useState(false)

  return (
    <div>
      <button onClick={() => setShowChart(true)}>Show Chart</button>
      <button onClick={() => setShowGrid(true)}>Show Grid</button>

      {showChart && (
        <Suspense fallback={<div>Loading chart...</div>}>
          <HeavyChart />
        </Suspense>
      )}

      {showGrid && (
        <Suspense fallback={<div>Loading grid...</div>}>
          <DataGrid />
        </Suspense>
      )}
    </div>
  )
}

Heavy chart libraries only download when users click the button. Each component has its own loading state. This dramatically improves initial load for feature-rich dashboards.

Webpack Magic Comments

Control chunk naming and prefetching.

// Named chunk
const module = await import(
  /* webpackChunkName: "analytics" */
  './analytics'
)

// Prefetch - download during idle time
const module = await import(
  /* webpackPrefetch: true */
  /* webpackChunkName: "settings" */
  './Settings'
)

// Preload - download with higher priority
const module = await import(
  /* webpackPreload: true */
  /* webpackChunkName: "critical" */
  './CriticalFeature'
)

Magic comments control bundler behavior. Named chunks produce readable filenames. Prefetch downloads chunks during browser idle time. Preload hints the browser to download with high priority alongside the parent chunk.

Measuring Bundle Impact

Verify code splitting is working.

# Analyze bundle with webpack
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json

# With Vite
npx vite build --mode production
# Check dist/ for multiple .js files

The bundle analyzer shows chunk sizes visually. Multiple small files instead of one large file confirms splitting works. Aim for an initial bundle under 200KB gzipped.

Best Practice Note

This is the same code splitting strategy we use in CoreUI to keep the library lightweight. Split at route boundaries first - it gives the biggest wins with the least effort. Then split large third-party libraries loaded conditionally. Avoid over-splitting: too many tiny chunks create excessive HTTP requests that cancel out the benefit. With HTTP/2, a few dozen chunks is fine. Profile with Lighthouse before and after to measure actual improvement.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
JavaScript Template Literals: Complete Developer Guide
JavaScript Template Literals: Complete Developer Guide

What is globalThis in JavaScript?
What is globalThis in JavaScript?

How to Open Link in a New Tab in HTML?
How to Open Link in a New Tab in HTML?

Dealing with Sass Deprecation Warnings in Angular 19
Dealing with Sass Deprecation Warnings in Angular 19

Answers by CoreUI Core Team