Using next/image to generate blurred image placeholders
Using the next/image
component to generate blurred image placeholders serverside is an easy way to provide useful information to your users before the image manages to load. However, it is not always possible to generate a blurred image placeholder for every type of images. When you are dealing with dynamic data such as images from an external source, this process becomes very dificult.
A simple example
This is a simple example of how to use the next/image
component to generate blurred image placeholders automatically.
import localImage from "public/localImage.png";
<Image src={localImage} placeholder="blur" />;
When a file is used as an image locally (i.e. not from an external source), the next/image
component will automatically generate a blurred image placeholder.
The problem with placeholder=“blur”
When using a local image, the next/image
component will automatically generate a blurred image placeholder. However, when using an external image or a local image as a string, the next/image
component will not generate a blurred image placeholder and requires the client to provide a blurred image placeholder using blurDataURL={'data:image/...'}
.
This makes fetching the blurred image placeholder very difficult. However, with a small amount of code, you can easily generate a blurred image placeholder for every type of image using sharp
.
Using sharp
to generate blurred placeholders on the server
Here is how it works
- Figure out if the image is local or external
- Fetch the image if its external or figure out the filepath if its local
- Generate a blurred image placeholder using
sharp
- Pass the blurred image placeholder to the client using
getStaticProps
orgetServerSideProps
Creating the blurred image
// utils/image.ts
const ROOT_PATH = process.cwd(); // The root path of the project
// Converts a buffer to a base64 data URL
const bufferToDataURL = (buffer: Buffer) => {
return `data:image/jpeg;base64,${buffer.toString("base64")}`;
};
export const getBlurDataURL = async (url: string) => {
let source: string | Buffer;
if (url.startsWith("http")) {
// Safe to assume this is an external image
source = Buffer.from(await (await fetch(url)).arrayBuffer());
} else {
source = path.join(ROOT_PATH, "public", url);
}
// Resize the image as small as possible per Vercel's recommendation
// I'm using 16:9 pixels as I know my images are using this ratio on the client
const image = await sharp(source)
.resize(16, 9, {
fit: "cover", // Resize the image to fit the ratio
})
.jpeg()
.toBuffer();
return bufferToDataURL(image);
};
Usage
// pages/index.tsx
export const getStaticProps = () => {
const data = await api.homePageData(); // Image url provided from external source or markdown file
return {
props: {
image: data.image,
blurDataURL: await getBlurDataURL(data.image),
},
};
};
interface HomeProps {
image: string;
blurDataURL: string;
}
const Home: NextPage<HomeProps> = ({ image, blurDataURL }) => {
return (
<div>
<Image src={image} placeholder="blur" blurDataURL={blurDataURL} />
</div>
);
};
export default Home;
There you go! Whenever you’re fetching data from an external source, you can use getStaticProps
or getServerSideProps
to generate a blurred image placeholder.