47
Blurred image placeholder with Next.js image and Cloudinary
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.
47