Crux Read real React/JS splitting snippets — a dynamic-import boundary, a lazy+Suspense layout, and a prefetch-on-hover handler — and pick the behaviour or the highest-leverage fix.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 13 min
Splitting bugs live in where the boundary sits and when the chunk is requested. Read each snippet, trace the request timing the browser actually performs, and choose the fix a senior would make first.
Goal
Practise reading split points the way you debug them in production: find where the dynamic import boundary really is, see when Suspense triggers a fetch, and spot the waterfall a hint would pre-empt.
Snippet 1 — where the boundary sits
// A: import at module top — bundled into the parent chunkimport { HeavyEditor } from "./HeavyEditor";function EditPanel({ open }) { if (!open) return null; return <HeavyEditor />;}// B: dynamic import inside the gateconst HeavyEditor = React.lazy(() => import("./HeavyEditor"));function EditPanel({ open }) { if (!open) return null; return ( <Suspense fallback={<Spinner />}> <HeavyEditor /> </Suspense> );}
Quiz
Completed
The editor is 200 KB and only opens when the user clicks Edit. What does each version actually ship, and which is right here?
Heads-up Tree-shaking removes unused exports at build time, not code reachable behind a runtime condition. A static top-level import is always bundled into the parent chunk; only a dynamic import() creates a split point.
Heads-up React.lazy is exactly the tool for a heavy, off-first-paint, interaction-gated widget. The anti-pattern is lazy-loading small or above-the-fold things, not deferring a 200 KB editor behind a click.
Heads-up A conditional render controls whether the component mounts, not whether its code is downloaded. Code arrives in the chunk it was bundled into — only import() moves it to a separate chunk.
On a 150 ms RTT phone, in what order are the Dashboard and Chart chunks fetched, and why does that matter?
Heads-up Declaring the lazy components together does not request them together. The inner chunk is only discovered after the outer one renders, so the fetches are sequential — that is the waterfall.
Heads-up Suspense fallbacks control what renders while waiting, not fetch order. The parent chunk is always discovered and fetched first; nesting cannot reverse the dependency.
Heads-up Both are on the initial render path here, so both are requested on load. The issue is not whether they load but that they load one after another.
What does the onMouseEnter handler accomplish, and what is the failure mode if you drop it?
Heads-up import() caches the module promise; the second call resolves from the same in-flight or completed request. The hover call warms the cache so the click does not pay the round trip.
Heads-up import() is still a split point — Settings stays a separate chunk. The handler only changes when that chunk is requested (on hover) without merging it into the parent.
Heads-up The handler fires a fetch and returns immediately; it does not block. It is a low-cost speculative prefetch that flattens the click-time waterfall.
Recap
Three things decide splitting behaviour in code: a static top-level import is always bundled into the parent chunk, so only import() creates a split point — and a runtime condition does not change that. Nested React.lazy boundaries are discovered sequentially, so each level adds a round trip on a high-RTT link. And firing import() early (on hover or idle) warms the chunk cache so the eventual mount skips the waterfall and the spinner. Read where the boundary sits and when the fetch is triggered, then move the trigger earlier rather than merging the chunk back in.