Skip to content

williamtobing/next-js-image-optimization

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

Next.js Image Component: A Complete Guide to Priority and Placeholders

Working with images in Next.js is a common task that can significantly impact your application's performance and user experience. The built-in next/image component offers powerful optimization features, but understanding how to use them effectively can be challenging. In this guide, we'll explore image priorities and placeholders in Next.js using a practical scenario: a gallery with 4 images where the first one is large and needs special handling.

Understanding Image Priority in Next.js

The priority attribute in Next.js Image component is a performance optimization that affects how the image loads.

import Image from 'next/image';

const HeroSection = () => {
  return (
    <Image
      src="/hero-image.jpg"
      alt="Hero Image"
      width={1200}
      height={600}
      priority
    />
  );
};

When you add priority to an Image component:

  1. The image is preloaded with high priority, starting to load as soon as possible
  2. The image is automatically marked as loading="eager" instead of the default loading="lazy"
  3. It removes the lazy loading behavior that normally defers loading until the image is near the viewport

When to Use Priority

Priority is particularly useful for:

  • Above-the-fold images that will be visible in the initial viewport
  • Large hero images
  • Any critical images that shouldn't wait for lazy loading

For our gallery example with 4 images, you might only want to set priority on the first, largest image:

<div className="gallery">
  {/* Only the first/main image gets priority */}
  <Image 
    src="/main-image.jpg"
    alt="Main Image"
    width={800}
    height={500}
    priority
  />
  
  {/* Other images load normally */}
  <Image src="/image2.jpg" alt="Image 2" width={400} height={300} />
  <Image src="/image3.jpg" alt="Image 3" width={400} height={300} />
  <Image src="/image4.jpg" alt="Image 4" width={400} height={300} />
</div>

Note: For images already in the initial viewport, adding priority isn't strictly necessary since browsers won't lazy-load them anyway. However, it can still be beneficial for the largest/most important image as it gives the browser a hint about resource prioritization.

Image Placeholders in Next.js

While images load (especially large ones), it's important to show placeholders to improve perceived performance. Let's explore the options Next.js provides.

Empty Placeholder (Default)

The simplest option is the empty placeholder - this is the default if you don't specify anything:

<Image 
  src="/large-image.jpg"
  alt="Featured Large Image"
  width={1200}
  height={800}
  placeholder="empty"  // This is the default, so you can omit it
/>

This simply shows empty space (the background color of the container) where the image will be until it loads. While simple, it's not very visually appealing.

The blurDataURL Placeholder

Next.js allows you to provide a tiny, blurred version of your image as a placeholder using the blurDataURL prop:

<Image 
  src="/large-image.jpg"
  alt="Featured Large Image"
  width={1200}
  height={800}
  placeholder="blur"
  blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII="
/>

But what exactly is a blurDataURL?

A blurDataURL is a base64-encoded image string that starts with data:image/.... It's typically a very small (1x1 pixel in the example above) image that represents the color or a blurred version of the full image. The browser can render this tiny image and stretch it to fill the space until the full image loads.

Simple Placeholders for Multiple Images

For our gallery with 4 images, we can use a simple color placeholder for all images:

import Image from 'next/image';

// Simple color placeholder - same for all images
const placeholderImg = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=";

const Gallery = () => {
  return (
    <div className="gallery">
      {/* First image loads with priority */}
      <Image 
        src="https://your-cloud-url.com/large-image.jpg"
        alt="Featured Large Image"
        width={1200}
        height={800}
        priority
        placeholder="blur"
        blurDataURL={placeholderImg}
      />
      
      {/* All other images use the same placeholder approach */}
      <Image 
        src="https://your-cloud-url.com/image2.jpg" 
        alt="Image 2" 
        width={400} 
        height={300}
        placeholder="blur"
        blurDataURL={placeholderImg}
      />
      <Image 
        src="https://your-cloud-url.com/image3.jpg" 
        alt="Image 3" 
        width={400} 
        height={300}
        placeholder="blur"
        blurDataURL={placeholderImg}
      />
      <Image 
        src="https://your-cloud-url.com/image4.jpg" 
        alt="Image 4" 
        width={400} 
        height={300}
        placeholder="blur"
        blurDataURL={placeholderImg}
      />
    </div>
  );
};

This approach is simple and effective - it uses the same color placeholder for all images, but still prioritizes loading the first large image.

Using an Image or Icon as Placeholder (Data URI Method)

If you want to use a custom image or icon as your placeholder, you can convert it to a data URI and use it as the blurDataURL:

import Image from 'next/image';

const Gallery = () => {
  // Your converted SVG/image as data URI
  const iconPlaceholder = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiPjxwYXRoIGQ9Ik01IDhoMTR2MTNINXptNS01djJoNHYtMmgtNHptLTUtMWgydjRoLTJ6bTItMmgydjJoLTJ6bTctMmg0djJoLTR6bTEwIDFoLTJ2NGgyeiIvPjwvc3ZnPg==";
  // This represents an image icon

  return (
    <div className="gallery">
      {/* First image with priority */}
      <Image 
        src="https://your-cloud-url.com/large-image.jpg"
        alt="Featured Large Image"
        width={1200}
        height={800}
        priority
        placeholder="blur"
        blurDataURL={iconPlaceholder}
      />
      
      {/* Other images with the same icon placeholder */}
      <Image 
        src="https://your-cloud-url.com/image2.jpg"
        alt="Image 2"
        width={400}
        height={300}
        placeholder="blur"
        blurDataURL={iconPlaceholder}
      />
      {/* Add more images... */}
    </div>
  );
};

To get your own image as a data URI:

  1. Use an online converter like Base64 Image Encoder
  2. Upload your SVG or small placeholder image
  3. Get the data URI (starting with data:image/...)
  4. Use that string as your blurDataURL

This approach is perfect for using your own custom placeholders with minimal code.

Advanced Placeholder Techniques

For more complex requirements, here are some advanced approaches:

Custom Image Component with Icon Placeholder

If you want to create a reusable component that shows a custom placeholder:

import Image, { ImageProps } from 'next/image';
import { useState } from 'react';

// Simple image component with icon placeholder
interface ImageWithPlaceholderProps extends ImageProps {
  placeholderSrc: string; // Path to your placeholder image/SVG
}

const ImageWithPlaceholder = ({ 
  placeholderSrc, 
  alt,
  ...props 
}: ImageWithPlaceholderProps) => {
  const [isLoaded, setIsLoaded] = useState(false);
  
  return (
    <div style={{ position: 'relative' }}>
      {!isLoaded && (
        <div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', backgroundColor: '#f5f5f5' }}>
          <img src={placeholderSrc} alt={`Placeholder for ${alt}`} style={{ width: '50px', height: '50px' }} />
        </div>
      )}
      <Image 
        {...props} 
        alt={alt}
        onLoadingComplete={() => setIsLoaded(true)}
        style={{ opacity: isLoaded ? 1 : 0, transition: 'opacity 0.3s ease', ...props.style }}
      />
    </div>
  );
};

// Usage
const Gallery = () => {
  return (
    <div className="gallery">
      <ImageWithPlaceholder 
        src="https://your-cloud-url.com/large-image.jpg"
        placeholderSrc="/image-placeholder-icon.svg"  // Your SVG in public folder
        alt="Featured Large Image"
        width={1200}
        height={800}
        priority
      />
      
      <ImageWithPlaceholder 
        src="https://your-cloud-url.com/image2.jpg"
        placeholderSrc="/image-placeholder-icon.svg"
        alt="Image 2"
        width={400}
        height={300}
      />
      {/* Add more images... */}
    </div>
  );
};

This component is more flexible, as it allows you to use any image from your public folder as a placeholder.

Advanced Custom Placeholder with Loading State

For even more control over the placeholder appearance and behavior:

import { useState } from 'react';
import Image from 'next/image';

const Gallery = () => {
  const [imageLoaded, setImageLoaded] = useState(false);

  return (
    <div className="gallery">
      {/* Container with relative positioning */}
      <div style={{ position: 'relative', width: '100%', height: 'auto' }}>
        {/* Custom placeholder shown when image is loading */}
        {!imageLoaded && (
          <div 
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              right: 0,
              bottom: 0,
              backgroundColor: '#f0f0f0',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <div className="loading-spinner">Loading...</div>
          </div>
        )}
        
        {/* The actual image */}
        <Image 
          src="https://your-cloud-url.com/large-image.jpg"
          alt="Featured Large Image"
          width={1200}
          height={800}
          priority
          onLoadingComplete={() => setImageLoaded(true)}
        />
      </div>
      
      {/* Other images */}
      <Image src="https://your-cloud-url.com/image2.jpg" alt="Image 2" width={400} height={300} />
      <Image src="https://your-cloud-url.com/image3.jpg" alt="Image 3" width={400} height={300} />
      <Image src="https://your-cloud-url.com/image4.jpg" alt="Image 4" width={400} height={300} />
    </div>
  );
};

This approach allows you to show custom loading indicators, spinners, or any other content while the image loads.

Generate Blur Hash for Cloud Images

For the most sophisticated placeholder experience, especially with remote images, you can use the plaiceholder or blurhash libraries to generate proper blur hashes:

// Server-side component or API route
import { getPlaiceholder } from 'plaiceholder';

// Generate blur data URL for remote image
const getBlurData = async (imageUrl: string) => {
  try {
    const { base64 } = await getPlaiceholder(imageUrl);
    return base64;
  } catch (error) {
    console.error('Error generating placeholder:', error);
    return null;
  }
};

// Then in your page component (Server Component)
async function GalleryPage() {
  // Get blur placeholders for images
  const mainImagePlaceholder = await getBlurData('https://your-cloud-url.com/large-image.jpg');
  
  return (
    <div className="gallery">
      <Image 
        src="https://your-cloud-url.com/large-image.jpg"
        alt="Featured Large Image"
        width={1200}
        height={800}
        priority
        placeholder="blur"
        blurDataURL={mainImagePlaceholder || undefined}
      />
      
      {/* Other images... */}
    </div>
  );
}

This approach generates high-quality, color-matched blur placeholders that look like blurred versions of the actual images. While more complex to set up, it provides the most professional-looking placeholders.

Conclusion

For our gallery with 4 images where the first one is large and needs priority loading, I recommend using one of these approaches:

  1. Simplest approach: Use priority on the first image and a simple color blurDataURL for all images
  2. Custom icon approach: Use the data URI method to show your custom icon as a placeholder
  3. Professional approach: For production apps, consider generating proper blur hashes for the best visual experience

The right choice depends on your specific requirements, design aesthetics, and the importance of the images to your user experience.

By effectively using these priority and placeholder techniques, you can create a much better user experience, especially when dealing with larger images that might take time to load.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published