DEV Community

Cover image for Lazy loading react components with dynamic imports and intersection observer
Siddharth Venkatesh
Siddharth Venkatesh

Posted on • Updated on

Lazy loading react components with dynamic imports and intersection observer

Lazy Loading

Lazy loading is a way by which we can load content only when they are needed. This is achieved by code-splitting, where we split our app into multiple chunks. The idea here is to serve the user with only the content they can view, and serve the other contents as and when the user visits them.

Route based code-splitting

For example, lets say we have a website where we have /home, /profile and /about routes, and /home is where the user first lands on. If we can compile the three routes into three bundles, we can serve them as and when the user visits the respective pages. The code for home route would contain only the code in <Home /> component. And when the user visits /about, the content for this route will be downloaded and displayed. If we have a fairly large app with lots of routes, this would give us a significant performance gain on initial page load times.

Component based code-splitting

The above example describes what is a route-based code-splitting strategy. We can take this a step further with a component based code-splitting strategy. Lets say we have a heavy form component buried deep in the app which the user would rarely use. It doesn't make sense for us to add it to our main bundle and it's a perfect recipe for lazy-loading.

Dynamic Imports

We can achieve this in React using Dynamic imports. React provides us way by which we can leverage dynamic imports with React.lazy and Suspense From React docs.

Lets build our example. We have a dummy form component <HeavyForm />. It does nothing, but you get the idea.
image

And, if we want to dynamically import it, we would do something like this
image

Intersection Observer

If you run the code now, you can see the HeavyForm is downloaded as a separate js file. This means that HeavyForm was bundled as a separate chunk and it is not part of our main bundle.

Great! But still, it is downloaded as soon as the page loads. We want this to be downloaded only when it is in the viewport, i.e when the user actually sees it.

This is where Intersection Observer comes in. IntersectionObserver lets us know if the target element is in the viewport or not. We can safely assume that if the IntersectionObserver fires, the target element is in the viewport. We can leverage this and lazily load any component when it is in the viewport.

I'm going to be using react-intersection-observer library, which uses native IntersectionObserver underneath and gives us neat hooks for ease of use.

image

This is what the complete implementation would look like with IntersectionObserver. react-intersection-observer gives us useInView hook, which gives us a ref and inView flag. The ref should be attached to the target element and inView lets us know if the target element is in the viewport. The threshold option is a value between 0 and 1 indicating the percentage of element that should be visible before triggering.

Now, <HeavyForm /> would only be downloaded when it is in the viewport.

Conclusion

This technique can be extended to multiple routes and components for easy gains on initial page load times. Remember to strike a balance between the components you lazy-load and components that are added to the main bundle. There is a penalty of network round trip that needs to be made when requesting lazy-loaded content.
Cheers!

You can take a look at the entire source code here

Top comments (6)

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

I've built a simple wrapper for that sometime ago - you can check it here -> github.com/maciekgrzybek/react-laz-y

Collapse
 
fahad07_khan profile image
Fahad Khan

There is different way of implementation for the same approach. Your code might look easier but which approach is better?

Collapse
 
maciekgrzybek profile image
Maciek Grzybek

They are pretty much the same, I just abstracted it out to its own component. Also I'm not saying that one is better than another :)

Collapse
 
zyabxwcd profile image
Akash

Great article man. Using intersection observer was really neat. I didn't know it had a react wrapper which made it so easy and clean as you said it.

Collapse
 
siddharthvenkatesh profile image
Siddharth Venkatesh

Thanks man. Glad you liked it.

Collapse
 
idrazhar profile image
Azhar Dev

Nice neat and simple tutorial.
intersection observer was completely new to me, but not anymore.