Code Splitting Widget
A compact panel that visualizes a JavaScript bundle's chunk composition. Pass in a flat list of chunks tagged with lazy?: boolean; the widget partitions them into an Initial tier (render-blocking) and a Deferred tier (lazy-loaded), stacks each tier into a proportional bar, and surfaces tier and chunk totals. Toggle a chunk between eager and lazy to watch the bars resize.
Preview
Bundle composition
Toggle vendor.js between eager and lazy to watch the tier bars resize.
Initial138 KB·36%
- core.js110 KB
- vendor.js28 KB
Deferred247 KB·64%
- dashboard.js/dashboard92 KB
- settings.js/settings74 KB
- billing.js/billing81 KB
Customize
Chunk sizes (KB)
110KB
28KB
92KB
74KB
81KB
Options
Installation
npx shadcn@latest add https://craftbits.dev/r/code-splitting-widget.jsonUsage
import { CodeSplittingWidget } from "@craft-bits/core";
<CodeSplittingWidget
title="Bundle composition"
chunks={[
{ id: "core", name: "core.js", size: 110 },
{ id: "vendor", name: "vendor.js", size: 28 },
{ id: "dashboard", name: "dashboard.js", size: 92, lazy: true, route: "/dashboard" },
{ id: "settings", name: "settings.js", size: 74, lazy: true, route: "/settings" },
]}
/>Render the "before" monolithic case by passing a single chunk:
<CodeSplittingWidget
chunks={[{ id: "main", name: "main.js", size: 385 }]}
/>Anatomy
- Header. Optional
title(renders with thecb-labelstyle) and adescriptionsub-line. Omit both for a chromeless panel. - Tier. One rounded card per tier. The tier label sits left; the tier total and percent share of the whole bundle sit right.
- Bar. A stacked horizontal bar where each segment's width is proportional to that chunk's share of its tier. Initial tier uses
--cb-accent; deferred uses the accent at 40% alpha so the eye reads it as the lower-priority tier. - Chunk row. One row per chunk under the bar — name + optional
route+ size intabular-numsso columns line up.
Understanding the component
- Partition. Chunks with
lazy: trueflow into the deferred tier; the rest land in the initial tier. The two tiers render in fixed order — Initial above Deferred — so the eye reads top-down as "what blocks render first". - Proportional widths. Within a tier, each segment's
widthischunk.size / tierTotal— the bar visualizes the internal split, not the absolute byte count. The tier total and percent share of the bundle live in the header so you can read both magnitudes at once. - Layout animation. Each chunk segment uses Motion's
layoutprop and theSPRINGS.snaptoken. When you toggle a chunk between lazy and eager, the bars resize smoothly. Underprefers-reduced-motionthe layout animation short-circuits and the bars snap. - Empty tier. If no chunk is deferred, the deferred tier renders at 60% opacity with a "no chunks in this tier" placeholder — the panel stays the same height so toggling doesn't reflow surrounding content.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
chunks | CodeSplittingWidgetChunk[] | required | Ordered list of chunks. |
title | ReactNode | — | Optional heading above the panel. |
description | ReactNode | — | Optional sub-headline under the title. |
unit | string | 'KB' | Display unit suffix for sizes. |
initialLabel | ReactNode | 'Initial' | Label for the render-blocking tier. |
deferredLabel | ReactNode | 'Deferred' | Label for the lazy tier. |
hideTotals | boolean | false | Hide the per-tier total + percent. |
headingAs | 'h2' | 'h3' | 'h4' | 'h3' | Tag for the title element. |
className | string | — | Merged onto the root via cn(). |
CodeSplittingWidgetChunk
| Field | Type | Description |
|---|---|---|
id | string | Stable identifier. |
name | ReactNode | Visible chunk label (e.g. core.js). |
size | number | Chunk size in the widget's unit. |
lazy | boolean | When true, the chunk lands in the deferred tier. |
route | ReactNode | Optional route or trigger annotation. |
Accessibility
- The wrapper is a
<section>withdata-cb-edu="code-splitting-widget". Tier cards exposedata-cb-tier="initial" | "deferred"anddata-cb-empty="true | false". - Chunk lists are
role="list"witharia-live="polite"so size updates are announced without interrupting the user. - Tier totals carry an
aria-labelliketier total 110 KBso the verdict is conveyed without depending on color. - The bar segments themselves are decorative (
aria-hidden) — the chunk list under each bar is the source of truth for assistive tech. - Layout animations short-circuit under
prefers-reduced-motion.
Credits
- Extracted from:
terminal-dreams(src/components/frontend-design/sdp-web-performance/ui/CodeSplittingWidget.tsx). The original was wired to a fixed-shapePerfContext(a singlecodeSplitPctslider that split a hardcoded 385 KB main bundle intocore.jsandroutes.js). This rewrite drops the context dependency and the two-bucket cap — consumers pass an arbitrary list of{ id, name, size, lazy?, route? }chunks so the widget can model any bundle, from a one-route SPA to a multi-route app with vendor and shared chunks.