Disclaimer - Please read the second part of this blog post here before proceeding. It explains the different data-fetching techniques that Next.js supports, and it guides you through the process of statically rendering a Next.js application page that fetches data at build time via the
getStaticProps function. If you just want to jump straight into this tutorial, then clone the project repository and install the dependencies.
Slow page loading times hurt the user experience. Anytime a user waits longer than a few seconds for a page's content to appear, they usually lose their patience and close out the page in frustration. A significant contributor to slow page loading times is image sizes. The larger an image is, the longer it takes the browser to download and render it to the page.
One way to improve the perceived load time of images (and by extension, page) is to initially show a placeholder image to the user. This image should occupy the same space as the intended image to prevent cumulative layout shifting. Additionally, compared to the intended image, this image should be much smaller in size (at most, several KBs) so that it loads instantaneously (within the window of the page's first contentful paint). The placeholder image can be as simple as a single, solid color (e.g., Google Images or Medium) or as advanced as a blurred representation of the image (e.g., Unsplash).
For Next.js application's, the
<Image /> component from
next/image augments the
<img /> HTML element by automatically handling image optimizations, such as...
Serving the correct image for a specific viewport size.
Preserving image positions in a layout.
Loading an image on-demand only when it enters the viewport.
Resizing, compressing and reformatting images.
These optimizations make the page's content immediately available to users to interact with. As a result, they help to improve not only a page's Core Web Vitals, but also the page's SEO ranking and user experience.
<Image /> components support blurred placeholder images. To tell Next.js to load an image with a blurred placeholder image, you can either...
Use the default
blurDataUrlprop. This prop only requires you to set the
<Image />component, but the image must be statically imported (cannot be images hosted on an external service). With this approach, Next.js automatically pre-generates a blurred representation of the image without us having to specify a Data URL for a blurred placeholder image.
Pass a custom Data URL of a blurred placeholder image to the
If the Next.js application happens to load images from an external service like Unsplash, then for each image, would we need to manually create a blurred placeholder image, convert the blurred placeholder image into a Data URL and pass the Data URL to the
With the Plaiceholder library, we can transform any image into a blurred placeholder image, regardless of where the image is hosted. The blurred placeholder image is a very low resolution version of the original image, and it can be embedded within the page via several methods: CSS, SVG, Base64 and Blurhash.
Blurhash is an algorithm that encodes a blurred representation of an image as a small, compact string. Decoding this string yields the pixels that make up the blurred placeholder image. Using these pixels, you can render the blurred placeholder image within a
<canvas /> element. Blurhash allows you to render a blurred placeholder image without ever having to send a network request to download it.
Below, I'm going to show you how to improve image loading in a Next.js application with the Plaiceholder library and Blurhash.
Installation and Setup#
To get started, clone the project repository and install the dependencies.
If you're coming from the second part of this tutorial series, then you can continue on from where the second part left off.
Within the project directory, let's install several dependencies:
plaiceholder- A library for generating blurred placeholder images.
@plaiceholder/next- A plugin for overriding Next.js's Webpack configuration to accommodate the use of Plaiceholder in a Next.js application.
react-blurhash- A React library for rendering a blurred placeholder image within a
<canvas />element from a Blurhash encoded string.
Then, wrap the Next.js configuration with
withPlaiceholder to extend the Next.js Webpack configuration so that Webpack excludes the
sharp image processing library from the output bundle.
images.domains in the Next.js configuration restricts the domains that remote images can come from. This protects the application from malicious domains while still allowing Next.js to apply image optimizations to remote images.
Transforming Unsplash Images to Blurred Placeholder Images#
Currently, the images on the home page are high resolution and take a lot of time to be fully downloaded. In fact, without any image processing, some of the raw Unsplash images are as large as 20 MBs in size.
However, by replacing these images with more optimized images, we can significantly improve the performance of the Next.js application. Using the
<Image /> component from
next/image and the Plaiceholder library, the home page will...
Initially render the blurred placeholder images.
Download the intended images (now formatted as WebP images) that are at most several hundred KBs large in size. These new, optimized images preserve as much of the image's original quality as possible and save bandwidth usage.
These changes should shrink the bars in the developer console's network waterfall chart.
To generate the blurred placeholder images, let's first modify the
<HomePage /> component's
getStaticProps() function so that these images get generated at build time. For each pet animal type, call the
getPlaiceholder() method of the Plaiceholder library with one argument: a string that references the image. This string can be a relative path or an absolute URL. Here, the string will be an absolute URL since the images come directly from Unsplash. From the
getPlaiceholder() method, we can destructure out any of the following:
css- An object that contains
backgroundRepeat. The CSS-based blurred placeholder image is comprised of a set of linear gradients.
base64- A Base64 string (
srcattribute of an
<image />element or
blurhash- An object that contains a Blurhash string (
hash) and the dimensions of the blurred placeholder image (
img- An object that contains the attributes that are required by an
<img />element (
For the Next.js application, we will destructure out
blurhash for a Blurhash-based blurred placeholder image and
img for information about the actual image.
Note #1: The
getPlaiceholder() method can accept a second, optional argument that lets you override the default placeholder size, configure the underlying Sharp library that Plaiceholder uses for image processing, etc. Check the Plaiceholder documentation here for more information.
Note #2: If you append the query parameters
fm=blurhash&w=32 to the URL of an Unsplash image, then the response returned will be a Blurhash encoded string for that Unsplash image.
Then, we need to update the
shared/interfaces/petfinder.interface.ts file to account for the newly added properties (
img) on the
After updating the
shared/interfaces/petfinder.interface.ts file, let's replace the image that's set as a CSS background image in the
<TypeCard /> component with two components:
<Blurhash />component from the
react-blurhashlibrary. This component will take the Blurhash string and dimensions from the pet animal type's
blurhashobject (from the
getPlaiceholder()method) and render a beautiful blurred placeholder image within a
<Image />component from
next/image. This component will take the attributes of the original image from the pet animal type's
imgobject (also from the
getPlaiceholder()method) and render an optimized version of the image (formatted as WebP, smaller in size, etc.).
Run the Next.js application in development mode:
When you visit the Next.js application in a browser, you will notice that the blurred placeholder images immediately appear. However, when the optimized images are loaded, you will notice that some of them are not aligned correctly within their parent containers:
To fix this, let's use some of the
<Image /> component's advanced props to instruct how an image should fit (via the
objectFit prop) and be positioned (via the
objectPosition prop) within its parent container. For these props to work, you must first set the
<Image /> component's
layout prop to
fill so that the image can grow within its parent container (in both the x and y axes).
objectPosition props correspond to the CSS properties
objectFit, set it to
cover so that the image occupies the entirety of the parent container's space while maintaining the image's aspect ratio. The outer portions of the image that fail to fit within the parent container will automatically be clipped out.
objectPosition, let's assign each pet animal type a unique
objectPosition that positions the pet animal within the image to the center of the parent container. Any pet animal type that is not assigned a unique
objectPosition will by default have
objectPosition set to
When you re-run the Next.js application, you will notice that the images are now correctly aligned within their parent containers.
When you check the terminal, you will encounter the following warning message being logged:
Since we specified the
layout prop as
fill for the
<Image /> component within the
<TypeCard /> component, we tell the
<Image /> component to stretch the image until it fills the parent element. Therefore, the dimensions of the image don't have to be specified, which means the
width props should be omitted.
Let's make the following changes to the
<TypeCard /> component in
Resizing an Unsplash Image with Imgix's URL API#
Unsplash leverages Imgix to quickly process and deliver images to end users via a URL-based API. Based on the query parameters that you append to an Unsplash image's URL, you can apply various transformations to the image, such as face detection, focal point cropping and resizing.
Given the enormous base dimensions of the Next.js application's Unsplash images (e.g., the dog image is 5184px x 3456px), we can resize these images to smaller sizes so that Plaiceholder can fetch them faster.
Since the images occupy, at most, a parent container that's 160px x 160px, we can resize the initial Unsplash images so that they are, at most, 320px in width (and the height will be resized proportionally to this width based on the image's aspect ratio). Let's append the query parameter
w=320 to the Unsplash image URLs in
When you re-run the Next.js application, you will notice that the Next.js application runs much faster now that Plaiceholder doesn't have to wait seconds to successfully fetch large Unsplash images.
If you find yourself stuck at any point during this tutorial, then feel free to check out the project's repository for this part of the tutorial here.
Proceed to the next part of this tutorial series to learn how to create pages for dynamic routes in Next.js.
If you want to learn more advanced techniques with TypeScript, React and Next.js, then check out our Fullstack React with TypeScript Masterclass.