Prefetching React Lazy

May 28, 23

How to prefetch React Lazy components or pages so that your page transitions are as smooth and fast as possible. This blog is in continuation of my earlier blog about Energetic React Lazy, where I discussed about code splitting and how you can use React Lazy to make your app lighter and faster.

Prefetching React Lazy - cover

What is Prefetching?

Prefetching is a technique with which some content is downloaded in the background, assuming that content will be used in the future, whether it is some images at the bottom of the page, any javascript assets required after the page is initially loaded, in code splitting’s case - downloading javascript assets for a page, that a user might visit next. For example, on YouTube, while on the home page, prefetching assets for video page, since it is most likely that a user will visit that page. Prefetching ensures that any content or assets that you might use in the future is already available (almost available) so that the load time is reduced. You can prefetch DNSs/CSS and JS assets/Images/almost anything, in many cases even data prefetching is also done.

Prefetching with React Lazy

Well if you again refer to this repo - https://github.com/maddhruv/react-lazy-example without the prefetching part, you might see that, while transitioning to a new page, you see the Suspense fallback component - the loader for a while - by the time the new page assets load, and then the react component is shown. The time for how long this loader would be available depends upon the size of the page assets.

Also, if you refer the last section of the blog Small React.lazy fix where I wrote a small wrapper function around React lazy to handle missing assets, we can tweak that a little bit to easily add support for prefetching.

// The original lazyWithRetry
export const lazyWithRetry = (componentImport) =>
lazy(async () => {
try {
return await componentImport();
} catch (error) {
console.error(error);
return window.location.reload();
}
});

With a very small modification we can add support for prefetching to this wrapper function

// Lazy with Retry and Prefetching
export const lazyWithRetryAndPrefetching = (componentImport) => {
const factory = async () => {
try {
return await componentImport();
} catch (error) {
console.error(error);
return window.location.reload();
}
};

const Component = lazy(factory);

Component.prefetch = factory;

return Component;
};

So now you see, with the lazy component we also add a method call prefetch , when called it just triggers the async function called factory to import the asset, but still doesn’t render it.

So now we have two ways, when the returned Component is rendered by React and react-router, it simply calls the React Lazy and does its job, but at the same time, you can call the prefetch method to fetch the assets manually whenever you want.

The easiest way and place I usually call this prefetch is at the useEffect of the main App component - where the react routes are, but you can call this prefetch on mouse overs, certain modals as well, basically anywhere.

// App.jsx
const Material = lazyWithRetryAndPrefetching(() => import("./Material"));

useEffect(() => {
Material.prefetch();
}, []);

🎉Tada, and that’s it, as soon as this parent component renders and this useEffect is triggered, it will start fetching the assets for the material page as well.

Checkout this PR for a complete diff on the example repo - https://github.com/maddhruv/react-lazy-example/pull/3/files

TypeScript Support

Well, if you are using TypeScript in your app, you might require an additional change, so as to make the react types understand that the React.lazy component has another method called prefetch.

To highjack these node_modules and third party codes, I use a wonderful package called patch-package that allows me to modify the dependencies in node_modules and make any custom patches I want.

So for these changes, you might need to patch your @types/react package, with something like this -

type LazyExoticComponent<T extends ComponentType<any>> = ExoticComponent<ComponentPropsWithRef<T>> & {
readonly _result: T;
+ // used to prefetch pages
+ prefetch: () => void
};

The exact location and type name may vary based on the version of @types/react package.

And yeah, enjoy the wonders of React and JavaScript, always pleasure sharing knowledge with you.