Lazy-load behind Suspense
Keep heavy, non-critical components out of the first bundle.
From frontend-patterns, section “Code Splitting & Lazy Loading”.
What loads before first click
Pick a network, press Load, compare.
Static importBad
Usable –Chart –JS 480 KB
React.lazy + SuspenseGood
Usable –Chart –JS 60 KB
import { lazy, Suspense } from 'react'
const HeavyChart = lazy(() => import('./HeavyChart'))
export function Dashboard({ data }) {
return (
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={data} />
</Suspense>
)
}
Fallback size decides the jump
Press Load chart and watch the Continue button.
fallback={null}BadCLS –
<Spinner /> 40pxBadCLS –
<ChartSkeleton />GoodCLS –
<Suspense fallback={<ChartSkeleton />}>
<HeavyChart data={data} />
</Suspense>
function ChartSkeleton() {
return <div className="h-[180px] rounded-lg bg-muted" aria-hidden />
}
Decoration loads quietly
Press Reload and notice where your eye goes.
fallback={<Spinner />}BadSpinner 0 s
fallback={null}GoodSpinner 0 s
const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
<Suspense fallback={null}>
<ThreeJsBackground />
</Suspense>
Why it matters, pros and cons
Why it matters
The first bundle runs before the page responds. Splitting defers a chart's cost.
Pros
- Less JS before the page is usable.
- Same-size skeleton keeps CLS at 0.
fallback={null}keeps decoration quiet.
Cons
- Slows anything needed at first paint.
- Small components aren't worth a round trip.
- A wrong-size skeleton still shifts the page.