Dynamic image placeholder in Next.js
A guide on how to create a dynamic image placeholder in Next.js
Sunday, November 14, 2021
How to create a dynamic image placeholder?
TL;DR
Check the full code here
Long version
Being able to see something on the screen immediately makes the app feel faster, either in a fast connection or a slow connection.
The GIF below shows what a user will see for an image loaded using a slow internet connection.
It gives the impression that something is wrong with our app.
Alternative
We can use the built-in placeholder image in Next.js, but for some cases like cover images, we might need something that resembles the actual image.
Check this blog for more info
Better but not enough. The placeholder did not load quickly enough to address the first issue. Also, The sudden change in colors makes it feel unnatural to the eye. However, we can create a custom placeholder for each image, but do we really need to?
In this blog post, I will show how to create a dynamic placeholder image in Next.js.
Here's the general steps on how to solve the issue
- Create placeholder metadata based on the image
- Create an SVG component from the placeholder metadata
- Create a container for the image and placeholder
- Unmount the placeholder image after the actual image is completed loading
- Putting all the components together
- End to end integration in a Next.js page
1. Create placeholder metadata based on the image
An easy way is to use plaiceholder
.js1234567891011
getPlaiceholder
returns a promise
of object with the following properties:
base64
blurhash
css
img
svg
For our purposes, we only need the img
and svg
property.
2. Create the svg component
The way to create the SVG component will depend on how placeholder metadata is created.
Here's a reference to plaiceholder
's version.
To better visualize how to create the SVG component, here is a sample SVG metadata
2.a. Create the svg
container
The first element in the SVG metadata is the svg
element.
The second element in the SVG metadata is the SVG properties.
.jsx12345678
2.b. Add the list of rect
s as svg
children
The third element in the SVG metadata is the list of rect
s, which will be rendered as svg
children.
.jsx12345678910111213
By doing step 2.a
and 2.b
, we can create a svg component that looks like this:
2.c. Blur the svg
The SVG can be blurred to remove the pixelated look.
.jsx12345678910111213
Applying step 2.c
will make the svg looks like this:
NOTE: Make sure to apply an appropriate filter value
For
svg metadata
with fewerrect
s, the result might looks like this:
3. Create a container; then, add the SVG and image to display
The svg
and Image
can be optionally wrapped in a another component(for styling).
Spread the img
props in the next Image
component.
NOTE: If you are using Next version 13+, import from
next/legacy/image
.jsx123456789101112
4. Unmount the placeholder image after the actual image is completed loading
Since the image is already loaded, the placeholder component can be unmounted.
Unmounting can be achieved using a useState
and the Image
's onLoadingComplete
callback method.
.jsx123456789
5. Putting all the components together
Here's the final Custom Image component with minor refactoring and default prop values:
.jsx1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
6. End to end integration in a NexJs page
Time to integrate our custom component in a NextJs application
.jsx12345678910111213141516171819202122232425262728293031
Here's the final result:
The web page seems to be loading faster even on a slow internet connection, and the transition of the image seems to be more natural.
Here's a local lighthouse score:
Conclusion
By adding a dynamic placeholder image, the users' experience will improve due to immediate feedback which gives the impression that the application is working faster. There's no need to stare at an empty screen while waiting for an image to load, especially on a slower network. Also, the transition seems to be more natural as the placeholder image is derived from the original image.