Display Placeholder While Image is Loading
CldImage wraps the Next.js Image component, thus giving us access to the placeholder API which can display an SVG image while the image itself is loading.
This helps for providing a great user experience, rather than an empty space, to help let the visitor know that image is loading.
To do this, we have several options:
placeholder="blur"
coupled with ablurDataURL
placeholder="..."
with the contents being a data URL
When working in the App Router, its currently recommended to wrap CldImage to make it easier to use in Server Components without having to opt an entire page in to a Client Component. Learn more
Blurred Images
To achieve a blurred image effect, we can first convert our Cloudinary image to a Data URL then pass it in to our CldImage component.
Inside of the App Router, we can utilize server components to generate a data URL from our image.
Within a server component, gerate the Data URL with:
import { getCldImageUrl } from 'next-cloudinary';
const imageUrl = getCldImageUrl({
src: '<Your Public ID>',
width: 100, // Resize the original file to a smaller size
});
const response = await fetch(imageUrl);
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const base64 = buffer.toString("base64");
const dataUrl = `data:${response.type};base64,${base64}`;
Then when rendering CldImage, utilize the dataUrl
using placeholder
and blurDataURL
:
import { CldImage } from 'next-cloudinary';
<CldImage
src="<Your Public ID>"
width="600"
height="400"
alt="Decriptive text"
placeholder="blur"
blurDataURL={dataUrl}
/>
Shimmer
Similar to the example from the Next.js documentation, we can create a shimmer effect when our images are loading.
Example from: https://github.com/vercel/next.js/blob/canary/examples/image-component/pages/shimmer.tsx (opens in a new tab)
Inside of the App Router, we can utilize server components to generate a data URL from our image.
Within a server component, gerate the Data URL with:
const shimmer = (w: number, h: number) => `
<svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="g">
<stop stop-color="#333" offset="20%" />
<stop stop-color="#222" offset="50%" />
<stop stop-color="#333" offset="70%" />
</linearGradient>
</defs>
<rect width="${w}" height="${h}" fill="#333" />
<rect id="r" width="${w}" height="${h}" fill="url(#g)" />
<animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
</svg>`
const toBase64 = (str: string) =>
typeof window === 'undefined'
? Buffer.from(str).toString('base64')
: window.btoa(str)
const dataUrl = `data:image/svg+xml;base64,${toBase64(shimmer(600, 400))}`;
Then when rendering CldImage, utilize the dataUrl
using placeholder
:
import { CldImage } from 'next-cloudinary';
<CldImage
src="<Your Public ID>"
width="600"
height="400"
alt="Decriptive text"
placeholder={dataUrl}
/>