Skip to content

Instantly share code, notes, and snippets.

@brophdawg11
Created July 29, 2022 14:03
Embed
What would you like to do?
React Router 6.4.0 Code Splitting Example
// Assume you want to do this in your routes, which are in the critical path JS bundle
<Route path="lazy" loader={lazyLoader} element={<Lazy />} />
// And assume you have two files containing your actual load and component:
// lazy-loader.ts -> exports the loader
// lazy-component.ts -> exports the component
// We'll render the component via React.lazy()
let LazyActual = React.lazy(() => import("./lazy-component"));
function Lazy() {
return (
<React.Suspense fallback={<p>Loading component...</p>}>
<LazyActual />
</React.Suspense>
);
}
// The loader is where things get interesting, we need to load the JS chunk
// containing our actual loader code - BUT we also want to get a head start
// on downloading the component chunk instead of waiting for React.lazy() to
// kick it off. Waterfalls are bad! This opens up the possibility of the
// component chunk finishing _before_ the loader chunk + execution. If that
// happens we don't want to see a small CLS flicker from Suspense since the
// component _is already downloaded_!
export async function lazyLoader(...args) {
let controller = new AbortController();
/*
* Kick off our component chunk load but don't await it
* This allows us to parallelize the component download with loader
* download and execution.
*
* Normal React.lazy()
*
* load loader.ts execute loader() load component.ts
* -----------------> -----------------> ----------------->
*
* Kicking off the component load _in_ your loader()
*
* load loader.ts execute loader()
* -----------------> ----------------->
* load component.ts
* ----------------->
*
* Kicking off the component load _alongside_ your loader.ts chunk load
*
* load loader.ts execute loader()
* -----------------> ----------------->
* load component.ts
* ----------------->
*/
import("./lazy-component").then(
(componentModule) => {
if (!controller.signal.aborted) {
// We loaded the component _before_ our loader finished, so we can
// blow away React.lazy and just use the component directly. This
// avoids the flicker we'd otherwise get since React.lazy would need
// to throw the already-resolved promise up to the Suspense boundary
// one time to get the resolved value
LazyActual = componentModule.default;
}
},
() => {}
);
try {
// Load our loader chunk
let { default: loader } = await import("./lazy-loader");
// Call the loader
return await loader(...args);
} finally {
// Abort the controller when our loader finishes. If we finish before the
// component chunk loads, this will ensure we still use React.lazy to
// render the component since it's not yet available. If the component
// chunk finishes first, it will have overwritten Lazy with the legit
// component so we'll never see the suspense fallback
controller.abort();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment