# Code splitting implemented at route level

- **Pattern:** `ab-002023` (`performance-deep-dive.bundle-analysis.code-splitting-routes`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002023
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002023)

## Why it matters

Without route-level code splitting, your admin dashboard, settings page, and checkout flow all ship to users who visit only your landing page. A monolithic bundle forces every visitor to download and parse code for routes they will never reach in that session. ISO 25010:2011 time-behaviour flags the slowdown; the practical result is a Time to Interactive that scales with your total codebase size rather than the complexity of the current page.

## Severity rationale

High because a single monolithic bundle forces every user to download and parse code for all routes simultaneously, making Time to Interactive proportional to total app size rather than the page being visited.

## Remediation

In Next.js App Router, each file in `app/` already produces a separate chunk automatically. For client components or shared heavy components that are route-specific, use `next/dynamic`.

```tsx
// app/admin/reports/page.tsx — heavy report component split off
import dynamic from 'next/dynamic'
const ReportChart = dynamic(() => import('@/components/report-chart'), { loading: () => <Skeleton /> })
```

Verify route splitting worked by checking `.next/static/chunks/` — you should see per-route files, not a single `app.js`.

## Detection

- **ID:** `code-splitting-routes`
- **Severity:** `high`
- **What to look for:** Count all routes/pages in the application. Enumerate how many produce separate JavaScript chunks in the build output vs. sharing a monolithic bundle. Examine the page/route structure and how code is imported. Look for dynamic imports using `React.lazy`, `import()`, or framework equivalents (Next.js dynamic imports, SvelteKit preload). Check the build output to confirm separate chunks exist for different routes.
- **Pass criteria:** Each route loads only its own code via code splitting. The build output shows separate `.js` chunks per route. Admin pages, settings, or heavy feature routes are not included in the initial bundle. At least 90% of routes should have separate chunks.
- **Fail criteria:** All route code is bundled together. A single monolithic `.js` bundle contains code for every page, even pages the user never visits.
- **Skip (N/A) when:** Never — route-level code splitting is a best practice for all multi-route applications.

- **Cross-reference:** For dynamic imports of heavy UI components within routes, see the `dynamic-imports-heavy-ui` check.
- **Detail on fail:** `"All route code bundled into single main.js (1.2MB gzipped). No per-route chunks detected in build output"` or `"Settings and admin routes included in initial bundle despite being gated behind authentication"`
- **Remediation:** Implement route-level code splitting so each route loads only its dependencies. In React, wrap route components with `React.lazy()` and `Suspense`. In Next.js, use dynamic imports for pages and components.

  ```tsx
  // app/admin/page.tsx — Next.js App Router auto-splits per route
  // For manual splitting: const AdminPage = React.lazy(() => import('./AdminPage'))
  ```

## External references

- iso-25010 time-behaviour

Taxons: performance

HTML version: https://auditbuffet.com/patterns/ab-002023
