How the React Suspense API lets us create a seamless image gallery adaptable for uncertain network conditions
Note: At the time of this writing, according to the official React documentation, the technique laid out in this tutorial is still in beta and is not recommended to be used in production, however React is planning to change this in the upcoming releases.
What is React Suspense?
React 16.6.0 has introduced the new Suspense API which now lets us simplify the way we handle asynchornous data calls when building components. The traditional way, without React Suspense, involved providing a state variable for a condition, when a data is being asynchronously loaded:
Basically what Suspense does is provide a fallback component, which is shown while the data is being loaded in the background. This way we do not need to provide a separate state variable to be able to show a temporary loader, while the async task is happening. This is especially useful when using stateless components.
Our goal for this tutorial is to build an image gallery that lazy loads images on the fly upon scrolling, but is also capable of showing a low resolution version of the desired image while waiting for the asynchronous high resolution image delivery. This allows us to show a downscaled version of the image for users with slower network speeds, thus providing a better and more appealing user experience.
This can be accomplished by utilising the react-cache package, which is built by the React team. It provides a simple caching solution for asynchronous tasks. Leveraging this package with the previously mentioned React Suspense API will give us the desired result.
Fetching images for the project
For this tutorial we will use placeholder images provided by https://picsum.photos/. This site lets us access random images with our desired resolution properties. Every time we run this app, we will query different images, specifying a low and a high resolution version for the same image. Then we will push this image array into state for later use.
Step 1: Building the image component using react-cache
Now let’s build our gallery. First step is to create a new img component, that will act just like the basic img tag, but in addition will provided an asynchronous image preload.
Step 2: Building the image wrapper using Suspense
Now we need to create an image wrapper component, that will hold all of our data inside it, plus it will get triggered to run only when scrolled into view. Note, that with the new Suspense API we are able to create this as a stateless component.
Notice in the code above, that there can be three possible scenarios when preloading this image, based on the network speed and content delivery speed:
- If the network is really really slow, and the component is unable to preload even a low resolution image fast enough, we should show a preloading element.
- Once the low resolution image is loaded, it is rendered above the preloading element, and we now see a blurry version of the final image, which is essentially just a lower resolution of it combined with a css blur filter property.
- The high resolution image is loaded, and is now replaced by the low resolution one.
We now have a fully functional React Suspense API driven image component, that is capable of delivering a fast callback solution to asynchronous lazy loading for users with slower network speeds.
Step 3: Wrapping all up
The final step is to trigger the render process of this component once it is scrolled into view. For this task there is a convenient package called react-inview-monitor, which provides us with different options for what to do with a component once it is scrolled into view. For this particular example we want to pass a child prop to our image component to signal that it is now ready to be initialised.
Success! We now have a lazy loading image gallery that provides a pleasant user experience regardless of the user’s network speed.
The full example
The complete example is available on my GitHub page to play around with.