Blur Placeholder Images with Next.js and Tailwind CSS

Davis Gitonga

Davis Gitonga / July 22, 2022

5 min read

Blur Placeholder Images with Next.js and Tailwind CSS

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
components/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:

components/BlurImage.tsx
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:

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.

components/BlurImage.tsx
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:

pages/index.tsx
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.

share your thoughts