An easy-to-use performant solution to lazy load images in React

Lazy loading images has been a common practice on the web for ages. It improves page speed and avoids loading assets which might never be shown to the user. However, I couldn’t find any good and performant solution for React that satisfied all my needs, so I wrote my own.

Introducing react-lazy-load-image-component

I created an npm package ready to be installed in any React application.

Just type:

npm i --save react-lazy-load-image-component

and you are ready to import it in your project.

Here you can see the simplest example of its usage:

import React from 'react';
import { LazyLoadImage } from 'react-lazy-load-image-component';

const MyImage = ({ image }) => (
  <div>
    <LazyLoadImage
      alt={image.alt}
      height={image.height}
      src={image.src}
      width={image.width} />
    <span>{image.caption}</span>
  </div>
);

export default MyImage;

It will load the image when it appears in the viewport after scrolling or resizing, but also when it becomes visible after a re-render. For example, in an image gallery with a filter, some images that were below the fold might appear in the viewport when a filter is applied.

Notice how the images are loaded when they appear on the screen, doesn’t matter if it’s because of the cards changing their order or because of a scroll event.

Some of its features are:

  • It throttles scroll and resize events by default. You can specify the delay time or use another delay function like debounce.
  • If attributes width and height are specified, it renders a placeholder of the same size as the image by default. That avoids the browser recalculating the layout when the image appears in the viewport and is loaded.
  • It allows setting a placeholder image, useful to load low resolution assets before downloading the full-resolution ones.
  • A custom placeholder component can be specified as well.
  • Comes with several on-visible effects like blur, black-and-white to color and opacity transitions. And it’s very easy to add new ones with CSS!
Images get color when the full-resolution version is loaded. That’s the black-and-white effect!
  • You can specify a threshold, so images start loading before they appear on the screen.
  • It includes beforeLoad and afterLoad events.
  • It’s standards-friendly, so you can specify any standard <img> attribute. In addition, it never produces something like <img src=""/>, which is not a valid markup.
  • There is no CSS by default and there are very few inline styles — which are basically used to set the size and background of the placeholder. So you probably will not have to fight against the default styles because they are minimal!

That’s cool, but I want to lazy-load a component which is not an image

No worries, LazyLoadComponent comes to the rescue! You can use it in a similar way than LazyLoadImage but you must provide a child that will be lazy loaded. It can be any other component or element.

import React from 'react';
import { ArticleContent, ArticleComments } from 'my-app';
import { LazyLoadComponent } from 'react-lazy-load-image-component';

const Article = ({ articleId }) => (
  <div>
    <ArticleContent id={articleId} />
    <LazyLoadComponent>
      <ArticleComments id={articleId} />
    </LazyLoadComponent>
  </div>
);

export default Article;

You can use most of the props you were using in LazyLoadImage like beforeLoad, afterLoad, threshold, etc.

Notice however, that in this case afterLoad will be called immediately after the placeholder is rendered. Use LazyLoadImage if you want it to get called after the image is loaded.

But I have an image gallery with hundreds of images, I don’t want all of them to listen to scroll events

In image galleries, trackWindowScroll might be handy to improve performance.

Scroll and resize events are tricky because they can be fired more than a dozen times in less than a second, that’s why the component comes with throttle by default.

However, if you have an image gallery or any other project with many components to lazy load, there is another way to improve performance. Instead of making every component subscribe to scroll/resize events, subscribe a parent of them.

That’s what the HOC trackWindowScroll does. Wrap the deepest common parent of the lazy load components with this HOC and it will receive a prop named scrollPosition. Pass it down to the lazy-loading children, and they will be clever enough to know they don’t need to subscribe to the scroll/resize events.

This way we can have dozens, hundreds or even thousands of components that lazy load while only one of them is listening to the scroll/resize events.

What’s next?

There are still many features that I would like to add, for example, supporting scroll events in a container element with overflow: scroll. But it’s enough for what I needed for now and will be happy to continue developing it or accepting PR if somebody is interested.


If you want to try it now download it from npm or get the code from GitHub. I also uploaded a live demo so you can see it in action without having to install it.

Let me know if you have any questions or suggestions!

Leave a Reply