Blur Placeholder Images with Next.js and Tailwind CSS
Davis Gitonga / July 22, 2022
5 min read •
Photo by mohammad alizade
Getting Started
To set up your next application, we can use create-next-app
to clone a Next.js and Tailwind CSS starter project from the official Next.js examples directory.
npx create-next-app -e with-tailwindcss blur-image
# or
yarn create next-app -e with-tailwindcss blur-image
This example includes:
- The latest version of Next.js
- TypeScript configured for Next.js
- An example Next.js API Route
- Prettier configured to format code and organize Tailwind CSS class names
- Tailwind CSS configured to strip away or purge unused class names.
Creating the Image Component
We can style our custom next image component now that Tailwind CSS is configured. On the project's root, create a components/BlurImage.tsx
folder with our react component with placeholder data.
mkdir components touch BlurImage.tsx
function Image() {
return (
<img src="https://bit.ly/placeholder-image" />
)
}
export default Image
Optimizing Image Component
We want to optimize images to help achieve good Core Web Vitals. These scores are a vital measurement of user experience on your website, and are factored into Google's search rankings. Next.js provides an Image Component that gives us some optimizations:
- Improved Performance: Images use modern image formats (like WebP & AVIF) for smaller file sizes and serve smaller images based on the device size
- Faster Page Loads: Images are only loaded once they enter the viewport
- Visual Stability: Prevents Cumulative Layout Shift automatically
- Asset Flexibility: On-demand image resizing, even for images stored on remote servers
Let's update our react component to use next/image
, and rename it to BlurImage
to prevent a naming collision with the Next.js Image
component we imported:
import Image from 'next/image';
function BlurImage() {
return (
<Image
alt=""
src="https://unsplash.com/placeholder-image"
layout="fill"
objectFit="cover"
/>
)
}
export default BlurImage
We'll use the fill layout and object-fit
CSS property to properly scale the image.
The Next.js Image component requires an allowlist to specify which external domains Next.js can optimize images from. While we're using unsplash.com
as a placeholder, we can add any domain we wish to the allowlist inside next.config.js
:
/**
* @type {import('next').NextConfig}
*/
module.exports = {
reactStrictMode: true,
images: {
domains: ['source.unsplash.com']
}
}
Adding Blurred Placeholder
When the image is loading, we'd prefer to show a blurred version for an improved user experience. While the Image component does include this for local image files, we will be retrieving the image from Unsplash. Instead, we can use onLoadingComplete
and CSS animation + blur to attain a similar effect.
import Image from 'next/image';
import { useState } from 'react';
function cn(...classes: string[]) {
return classes.filter(Boolean).join(' ');
}
function BlurImage({ src, ...props }) {
const [isLoading, setLoading] = useState(true);
return (
<Image
{...props}
src={src}
layout="fill"
objectFit="cover"
className={cn(
'duration-700 ease-in-out',
isLoading
? 'grayscale blur-2xl scale-110'
: 'grayscale-0 blur-0 scale-100'
)}
onLoadingComplete={() => setLoading(false)}
/>
);
}
export default BlurImage;
onLoadingComplete
is a callback function that is invoked once the image is completely loaded.
We also use a simple utility function cn
for conditionally joining classNames together.
Now, let's display the images on our index page. Update the index page with the code below:
import Head from 'next/head';
import Image from 'next/image';
import BlurImage from '../components/BlurImage';
import styles from '../styles/Home.module.css';
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
</Head>
<main className={styles.main}>
<h1 className={styles.title}>
<a href="https://nextjs.org">Next.js!</a> Image Blur with Tailwind CSS
</h1>
<div className={styles.grid}>
<div className={styles.card}>
<h3>Without Blur Placeholder</h3>
<Image
src="https://source.unsplash.com/random/400x200/?city"
alt=""
width={400}
height={200}
/>
</div>
<div className={styles.card}>
<h3>With Blur Placeholder #1</h3>
<BlurImage
src="https://source.unsplash.com/random/400x200/?web"
alt=""
width={400}
height={200}
/>
</div>
<div className={styles.card}>
<h3>With Blur Placeholder #2</h3>
<BlurImage
src="https://source.unsplash.com/random/400x200/?nature"
alt=""
width={400}
height={200}
/>
</div>
<div className={styles.card}>
<h3>With Blur Placeholder #3</h3>
<BlurImage
src="https://source.unsplash.com/random/400x200/?galaxy"
alt=""
width={400}
height={200}
/>
</div>
</div>
</main>
</div>
);
}
Blur Image in Action
Our example is now showing a list of images, fetched from Unsplash, and displayed nicely using Tailwind CSS.
Below is a StackBlitz project with the full code for our above example:
Blur with next/image Component
This option only supports local images (stored in the public
folder of your app.)
The placeholder
prop on the next/image
component provides a similar placeholder like the one we've just built that is used while the image is loading. Possible values are blur
or empty
. Defaults to empty.
When blur
, the blurDataURL
property will be used as the placeholder. If src
is an object from a static import and the imported image is .jpg
, .png
, .webp
, or .avif
, then blurDataURL
will be automatically populated.
For dynamic images, you must provide the blurDataURL
property. Solutions such as Plaiceholder can help with base64-encoded
image generation.
When empty, there will be no placeholder while the image is loading, only empty space.
Try it out:
Conclusion
In this tutorial, we were able to create a blurred placeholder from an external image source (Unsplash) with Tailwind CSS and next/image
component. we also mentioned the out of the box option provided by the next/image
component using the placeholder="blur"
prop.