React utilities for lazy image loading
npm install react-lazy-images> Components and utilities for lazy image loading in React.




- Features
- Install
- Motivation
- Usage
- Examples
- API Reference
- Feedback
- Roadmap
- Contributing
- Thanks
- License
- Composable pieces, preloading images and handling failures.
- Full presentational control for the caller (render props).
- Modern, performant implementation, using IntersectionObserver and providing polyfill information.
- Eager loading / Server-side rendering support.
- Debounce / Delay; can wait for an image to be in the viewport for a set time, before loading.
- Works with horizontal scrolling, supports background images.
- Fallbacks for SEO / when Javascript is disabled.
- Easy to understand source code. You should be able to fork and do your thing if desired.
- Ample documentation to help you understand the problem, in addition to the solutions.
What it does not do by itself:
- Polyfill IntersectionObserver. Adding polyfills is something you should do consciously at the application level. See Polyfilling IntersectionObserver for how to do this.
- Dictate the kind of placeholders displayed. There are many ways to do it; you can use a simple box with a background color, a low-resolution image, some gradient, etc.
In other words, this library focuses on loading the images once in view and supporting loading patterns around that.
The presentational patterns are yours to decide!
Fear not though, we cover both patterns in the examples section.
This package is distributed via npm.
``shell`
$ npm install --save react-lazy-imagesor
$ yarn add react-lazy-images
Then import according to your modules model and bundler, such as Rollup and Webpack:
`js
// ES Modules
// For all possible functions to import look at the documentation
import { LazyImage } from "react-lazy-images";
/// CommonJS modules
const { LazyImage } = require("react-lazy-images");
`
A UMD version is also available on unpkg:
`html`
Browsers preload images; as soon as they encounter an tag with a valid src, they kick off the request for the image (they even do this before the HTML has been parsed).
Even in cases where a certain image is not in the viewport, it will be requested.
This can have adverse effects for users, especially on mobile or metered connections.
This brings us to the basic premise of any Lazy Image Loading library:
- Have a way to observe the visibility of the DOM elements
- Prevent the browser from loading images directly
- Once an image is in view, instruct the browser to load it and place it in the element
In vanilla JS, this means "hiding" the actual src in a data-src attribute, and using classes to indicate state, e.g. .isLazyLoaded .lazyLoad.data-src
On initialisation, a script queries for these classes and attributes, keeps track of visibily, and swaps with an actual src, kicking off the browser request process.
It can elect to preload the Image, and only swap once loaded.
With React, all this implicit state management is brought into one place, since you do not have to stash loading information in the DOM and pick it back up again.
This can potentially mean a nicer, more composable codebase, and it was one of the main design goals for this library.
The way to do this visibility tracking has for the most part been listening for events such as scroll.
This is synchronous by nature and can have performance implications.
It also involves calling getBoundingClientRect() to calculate the interesection of the image with the viewport; this function causes relayout.
This was the motivation for browsers providing IntersectionObserver.
Using this API is not specific to React; it just seems like a good fit for this task nowadays.
If you want to just dive in, do this:
`jsx
import { LazyImage } from "react-lazy-images";
alt="Buildings with tiled exteriors, lit by the sunset."
placeholder={({ imageProps, ref }) => (

)}
actual={({ imageProps }) => }
/>;
`
:warning: It is important that you pass on the ref in placeholder, otherwise the detection of the element intersecting is impossible. :warning:
Note that while you can set the rendered components to be anything you want, you most likely want to use the same src, srcSet and alt attributes in an eventually.
To keep this consistent, and reduce repetition, the render callbacks pass those attributes back to you.
You can play around with this library on Codesandbox.
Additionally, make sure you understand how to polyfill IntersectionObserver and strategies for when JS is not available.
From then on:
- If you want to learn more about the API and the problem space, read the rest of this section.
- If you need more fine-grained rendering, read about LazyImageFull.
- If you want to list the props, see the API reference.
The render prop pattern is used throughout in LazyImage.LazyImage
The component handles the behaviour of tracking when the image is in view, but leaves the actual rendering up to the consumer.
Thus, whether you want to display a simple , your own , or even wrapped elements, it is simple to do so:
`jsx
alt="Buildings with tiled exteriors, lit by the sunset."
// This is rendered first, notice how the src is different
placeholder={
({imageProps, ref}) =>

}
// This is rendered once in view; we use the src and alt above for consistency
actual={
({imageProps}) =>
}
/>
// Perhaps you want a container?
alt="Buildings with tiled exteriors, lit by the sunset."
placeholder={
({imageProps, ref}) =>

These props are there to instruct the component what to render in those places, and they take some useful information (in this case, a className) from the LazyImage.
$3
LazyImage should work for most cases, but you might need more fine-grained rendering.
One use case would be doing animations with CSS transitions, where re-rendering the component (which LazyImage does) would not be sufficient.
In those cases, consider LazyImageFull:`jsx
import { LazyImageFull, ImageState } from "react-lazy-images";// Function as child
//
src, alt and srcSet are passed back to the render callback for convenience/consistency
{({ imageProps, imageState, ref }) => (
{...imageProps}
ref={ref}
src={
imageState === ImageState.LoadSuccess
? imageProps.src
: "/img/porto_buildings_lowres.jpg"
}
style={{ opacity: ImageState.LoadSuccess ? "1" : "0.5" }}
/>
)}
;
`This component takes a function as a child, which accepts
{src, srcSet, imageState}.
The various image states are imported as {ImageState}, and you can conditionally render based on them.This technique can give you more fine-grained rendering if needed, but can potentially be more verbose.
Any of the presentational patterns presented that are possible with
LazyImage are also possible with LazyImageFull.
(The opposite is not necessarily true, or at least has more duplication).src/LazyImage.tsx, you will see that LazyImage is implemented in terms of LazyImageFull!$3
A common optimisation to the loading strategy is to preload the image before swapping it for the placeholder.
In other words, once the image is in view, you can kick off a request to load the image, and only show it once fully loaded.
This avoids presenting a half-loaded image (i.e. one that is still scanning top-to-bottom), and makes the transition smoother.
This behaviour is provided with the
src prop:`jsx
// Note that the actual src is also provided separately,
// so that the image can be requested before rendering
src="/img/porto_buildings_large.jpg"
alt="Buildings with tiled exteriors, lit by the sunset."
placeholder={
({imageProps, ref}) =>
LazyImage-Placeholder}">

}
actual={
({imageProps}) =>
LazyImage-Actual}>
![]()
}
/>
`There is another case if you are using
srcset for your images; LazyImage needs that information to preload the correct image. You can provide it with the srcSet prop.$3
You can choose what to display on Loading and Error using the render props
loading and error:`jsx
src="/image/brokenimagenotherewhoops.jpg"
alt="Buildings with tiled exteriors, lit by the sunset."
actual={({ imageProps }) =>
}
placeholder={({ ref }) => }
loading={() => (
Loading...
)}
error={() => (
There was an error fetching this image :(
)}
/>
`$3
What does SSR even mean in a lazy images context?
If you recall the basic premise, then you will know that we "hide" the intended image and display a placeholder.
For the actual image request to kick off, Javascript has to have loaded, and detected that the image is in the viewport.
In cases where you are server-side rendering, there can be a non-neglible amount of time until Javascript is available (i.e. it has to download, parse, execute).
For those cases, it would be beneficial if we can mark images to render with the intended/final src by default, so that the browser can start requesting them as soon as it gets the HTML.
This behaviour is available by using a
loadEagerly prop:`jsx
loadEagerly
src="/img/porto_buildings_large.jpg"
alt="Buildings with tiled exteriors, lit by the sunset."
placeholder={({ imageProps, ref }) => (

)}
actual={({ imageProps }) =>
}
/>
`While the usage is simple, the patterns in your app will not necessarily be so.
Think about the cases where it is beneficial to do this, and apply it with intent.
Examples might be eager-loading hero images, preloading the first few elements in a list and so on.
Some of these use cases are provided as examples.
$3
In cases where you have a long list of images that the user might scroll through, then loading intermediate images can waste bandwidth and processing time.
This is undesired.
The way to handle it is with a minimum duration that the image has to stay within the viewport, before making the request.
This is specified using the
debounceDurationMs prop:`jsx
src="/img/porto_buildings_large.jpg"
alt="Buildings with tiled exteriors, lit by the sunset."
debounceDurationMs={1000}
placeholder={({ imageProps, ref }) => (

)}
actual={({ imageProps }) =>
}
/>
`$3
If Javascript is disabled altogether by the user, then they will be stuck with the placeholder (and any images loaded eagerly).
This is probably undesirable.
There are a few strategies for fallbacks.
Most of them are variations on a