Make Your Image Loading Blurry in Next.js

Furkan Cengiz
4 min readNov 16, 2023

--

Images on the web matter in terms of site performance and to be frank, they are painful. One of the best features of Next.js is the Image component that optimizes images for you. It automatically generates images in modern formats like WebP for different screen sizes to reduce image sizes, lazy loading to load images when they enter the viewport, and visual stability to prevent layout shifts. If you’re not familiar with the Image component, I recommend you watch this video by Lee Robinson. In this article, I want to focus on adding blurred images while the actual images load.

the Default Image Blur of the Image Component

Even though there is one limitation, the Image component can even generate blurred images out of the box! Let me show you an example from my portfolio website.

Default Blurred Image from my portfolio

It’s way better than the default loading. As I stated above, there is a limitation: you need to statically import the image. It may not be possible to import images statically especially if you’re using dynamic images or cloud services. We’ll overcome this limitation in the following technique but now let’s see the code that makes possible this default blurred image.

import furkanpicture from "@/public/pics/furkan.png";
import Image from "next/image";
import React from "react";

export default function AboutImage() {
return (
<figure>
<Image
src={furkanpicture}
alt={"Furkan Cengiz"}
placeholder="blur" //it's "empty" by default
/>
</figure>
);
}

When the placeholder prop is passed as blur, and the src prop is passed as a statically imported image object, the Image component generates blurred images as you see in the video! If you’re building a static page or website, you can leverage this default blur behavior for sure!

Generating dataURL Using Plaiceholder

We are able to add blurred images for loading states in case we’re using static images. Now, we need to overcome the limitations of the previous method.

As stated in the docs, we must pass dataURL to the blurDataUrl property of Image component for dynamic images. The dataURL is a string that basically holds the blurred image data in base64 format.

here's an example of dataURL
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAJ0lEQVR4nGPY2fXjv458/H9Bbtf/IDbD/7v//8/Mvfq/J+nEfxAbAF3NFsFiuaE1AAAAAElFTkSuQmCC

Now, we know what we need but how do we generate dataURL like this? Plaiceholder, as suggested in the docs, makes this possible for us!

First off, we need to install two packages: sharpand plaiceholder. Please follow this page for installations.

I assume that you’re using Next.js. Please follow this page to configure your next.config.ts or next.config.mjs file.

The installation is done! Now, we need to implement a custom function that runs on the server side and generates dataURLs which’s exactly what we need.

import fs from "node:fs/promises";
import { getPlaiceholder } from "plaiceholder";

export const getBase64 = async (imgPath: string) => {
try {
const file = await fs.readFile(`public/${imgPath}`)
const { base64 } = await getPlaiceholder(file)
return base64
} catch (error: unknown) {
//error handling
if (error instanceof Error) return error.message
else if (error && typeof error === "object" && "message" in error)
return error.message as string
else if (typeof error === "string") return error;
else return "Unexpected error!"
}
}

I implemented using the async-await keywords instead of using then or catch methods as in docs. We first need to read the file that the getPlaiceholderfunction needs. I added a prefix as public since I’m sure that every file path has public prefix. Then, we pass the file to thegetPlaiceholderfunction. It returns different objects like css, color, pixels, base64, and more. Destructure returned object to extract base64 value and return it!

The function we need to generate dataURLs is complete. Here comes the last important step!

Using getBase64 function

Notice that we read a file in the function importing fsfunction from node:fs/promises which means this function can only run on the node environment. So, it’s important to note that this function cannot be used on client components. However, there’s no need to worry just yet, as we do have server components. Although you are not able to use this function on client components, you can still call this async function in a server component and pass the returned value down to the client components.

You can see an example from my portfolio project below. (If you’re already using your Image component in a server component, you don’t need this last step, just make your component async, call the function, and pass the base64 string to blurDataUrl prop)

In the code above, I’m generating multiple base64 strings which are passed down into individual client components. (You can generate a single base64 string just by passing the path string of your single image to the getBase64 function and await it)

That’s it, we have our blurred images ready!

Last but not least, there are other techniques such as SVG and color that you can leverage from using plaiceholder. You may want to try them as well, and visit all techniques presented in the demo by plaiceholder.

Blurred images in loading states are something that I really love. It’s a technique that many websites and even Windows applications such as Discord use, and I think it adds a touch of elegance. I hope this article was helpful. To stay updated on my latest work, follow me on Medium or LinkedIn. Looking forward to seeing you in the next one!

--

--