Load heavy code on use
Keep heavy, gated and third-party code out of the main chunk.
From vercel-react-best-practices: bundle-dynamic-imports, bundle-conditional and 1 more.
Monaco in the main chunk
Load both, then click Open editor before each hydrates.
Static importBad
main.js435 KB
Not loaded
import { MonacoEditor } from './monaco-editor'next/dynamicGood
main.js135 KB
monaco-editor.jsnot requested
Not loaded
dynamic(() => import('./monaco-editor'), { ssr: false })import dynamic from 'next/dynamic'
const MonacoEditor = dynamic(
() => import('./monaco-editor').then(m => m.MonacoEditor),
{ ssr: false, loading: () => <EditorSkeleton /> }
)
function CodePanel({ code }: { code: string }) {
const [open, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen(true)}>Open editor</button>
{open && <MonacoEditor value={code} />}
</>
)
}
A feature most leave off
Toggle each switch on, off, then on again.
In the initial payloadBad
- Initial JS
- 315 KB
- animation-frames.js
- in main.js
import { frames } from './animation-frames.js'import() on enableGood
- Initial JS
- 135 KB
- animation-frames.js
- not requested
import('./animation-frames.js')function AnimationPlayer() {
const [enabled, setEnabled] = useState(false)
const [frames, setFrames] = useState<Frame[] | null>(null)
useEffect(() => {
if (enabled && !frames && typeof window !== 'undefined') {
import('./animation-frames.js')
.then(mod => setFrames(mod.frames))
.catch(() => setEnabled(false))
}
}, [enabled, frames])
return (
<>
<Switch checked={enabled} onCheckedChange={setEnabled} />
{enabled && (frames ? <Canvas frames={frames} /> : <Skeleton />)}
</>
)
}
Analytics after hydration
Drag the third-party size, then Replay.
Static importBad
HTML
main.js
hydrate
analytics
Interactive at 0.00 s
import { Analytics } from '@vercel/analytics/react'dynamic, ssr: falseGood
HTML
main.js
hydrate
analytics
Interactive at 0.00 s
dynamic(() => import('@vercel/analytics/react')…, { ssr: false })import dynamic from 'next/dynamic'
const Analytics = dynamic(
() => import('@vercel/analytics/react').then(m => m.Analytics),
{ ssr: false }
)
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
Why it matters, pros and cons
Why it matters
Buttons ignore clicks until the main chunk loads. Keep one-panel code out of it.
Pros
- Earlier Time to Interactive
- Unused features are never downloaded
- Split chunks cache independently
Cons
- First open waits a round trip; preload on hover
- Never lazy-load the LCP element
- Tiny splits add requests; split at tens of KB