DEV Community

Cover image for Blurred image placeholder with Next.js image and Cloudinary
Nicolas Erny
Nicolas Erny

Posted on

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,
  },
};
Enter fullscreen mode Exit fullscreen mode

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

React code example

It would be nice to display a blurry image while the browser loads the real one.
Blurred image placeholder
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}`;
}
Enter fullscreen mode Exit fullscreen mode

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,
            },
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

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>
    );
}
Enter fullscreen mode Exit fullscreen mode

You can find the source code on Github.
This has been helpful for me in my projects. Hopefully it helps you as well.

Oldest comments (6)

Collapse
 
harshboricha98 profile image
Harsh boricha • Edited

Thank you for the solution. I found another solution in cloudinary.com/documentation/react....

<AdvancedImage cldImg={imageurl} alt="" plugins={[lazyload(), placeholder()]} />

this does the work just fine. I used your approach as well but was getting slightly less performance.

Collapse
 
nicolaserny profile image
Nicolas Erny

Thanks for sharing. I will give it a try.

Collapse
 
siegfriedbz profile image
Siegfried Bozza

Thank you for sharing
as opposed to using getStaticProps which runs server side , if we use AdvancedImage from '@cloudinary/react', do we benefit from SSG ? or is it running only on the client ?

As I understand, AdvancedImage from '@cloudinary/react' is to be used in a React app, not in Next.js ?

Collapse
 
lekipising profile image
Liplan Lekipising

Thanks for this informative and great solution. I would like to know the response time of the blurred image.

Collapse
 
nicolaserny profile image
Nicolas Erny

I don't have any figures about the response time (I will try to get some if I have time).
But here are some ideas about the blurred image:
As the blurred image is generated at compile time, we don't have any latency issues on the site. However, it's important to have a base64 blurred image as small as possible (according to Next Image documentation).
To do that, you can play with the Cloudinary parameters (w_100/e_blur:1000,q_auto,f_webp). Reducing the image size and the quality is the way to go.

Collapse
 
lekipising profile image
Liplan Lekipising

Awesome, thanks!