Blurred image placeholder with Next.js image and Cloudinary
Nicolas Erny
Posted on December 2, 2021
Most of the time, we don't want to manage image optimizations ourselves. Libraries such as Next.js/image are handy to deal with responsive images. In addition to Next.js image, I often use Cloudinary, which is a media management service.
Two key benefits:
- Use a CDN dedicated to our images
- Easy to apply transformations to an image: a URL-based image API
Next.js provides excellent integration with Cloudinary. For example, we can use the following next.config.js file.
const cloudinaryBaseUrl = `https://res.cloudinary.com/${process.env.CLOUDINARY_CLOUD_NAME}/image/upload/`;
module.exports = {
images: {
loader: "cloudinary",
path: cloudinaryBaseUrl,
},
};
where the CLOUDINARY_CLOUD_NAME env variable contains our cloudinary cloud name.
We're ready to build a basic app to display an image using next/image and Cloudinary. Here's what a React code would be like:
function Home({ exampleImage }) {
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>Blurred image placeholder</h1>
<h2 className={styles.subtitle}>with Next.js image and cloudinary</h2>
<div className={styles.imagewrapper}>
<Image
src={exampleImage.src}
alt="Example"
width="1920"
height="1280"
layout="responsive"
quality="75"
sizes="60vw"
/>
</div>
</main>
</div>
);
}
It would be nice to display a blurry image while the browser loads the real one.
But, unfortunately, next/image does not generate the blurred placeholder automatically when we use the Cloudinary loader.
Let's try to add a blurred image placeholder. Next/image provides two properties: placeholder and blurDataURL. We will rely on Cloudinary to get a low-quality, blurred picture. It leads to the following function to generate a base64 encoded data URL:
export async function getBase64ImageUrl(imageId: string): Promise<string | undefined> {
const response = await fetch(`${process.env.CLOUDINARY_BASE_URL}w_100/e_blur:1000,q_auto,f_webp${imageId}`);
const buffer = await response.arrayBuffer();
const data = Buffer.from(buffer).toString('base64');
return `data:image/webp;base64,${data}`;
}
Finally, we have to generate the data URL at compile-time. With Next.js, it's pretty straightforward by implementing the getStaticProps function:
export async function getStaticProps() {
const imageSrc = process.env.CLOUDINARY_EXAMPLE_IMAGE_SRC;
if (!imageSrc) {
throw new Error('Missing CLOUDINARY_EXAMPLE_IMAGE_SRC env variable');
}
const blurDataUrl = await getBase64ImageUrl(imageSrc);
return {
props: {
exampleImage: {
src: imageSrc,
blurDataUrl: blurDataUrl,
},
},
};
}
where the CLOUDINARY_EXAMPLE_IMAGE_SRC env variable contains our cloudinary image id.
So here's the final version of the React code:
function Home({ exampleImage }: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>Blurred image placeholder</h1>
<h2 className={styles.subtitle}>with Next.js image and cloudinary</h2>
<div className={styles.imagewrapper}>
<Image
src={exampleImage.src}
alt="Example"
width="1920"
height="1280"
layout="responsive"
quality="75"
sizes="60vw"
placeholder="blur"
blurDataURL={exampleImage.blurDataUrl}
/>
</div>
</main>
</div>
);
}
You can find the source code on Github.
This has been helpful for me in my projects. Hopefully it helps you as well.
Posted on December 2, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.