If you’ve ever run a Lighthouse audit on a Next.js site, you’ve likely seen the dreaded ‘Efficiently encode images’ or ‘Largest Contentful Paint image was slowly loaded’ warnings. In my experience, images are almost always the primary culprit behind sluggish page loads. That’s why I’ve put together this next.js image component optimization tutorial to help you move beyond the standard <img> tag and start leveraging the powerful next/image component.
The next/image component isn’t just a wrapper; it’s a sophisticated image orchestration tool that handles resizing, optimization, and serving images in modern formats like WebP and AVIF automatically. When combined with a strategy for introduction to image lazy loading, you can drastically reduce your page weight.
Prerequisites
- A functional Next.js project (version 13+ using App Router is recommended).
- Basic familiarity with React and CSS.
- A few high-resolution images (JPEG/PNG) to test optimization.
Step 1: Switching to the Image Component
The first step is replacing all your standard HTML <img> tags with the Next.js Image component. The most critical difference is that Next.js requires you to define the width and height to prevent Layout Shift (CLS).
import Image from 'next/image'
import profilePic from '../public/me.png'
export default function Page() {
return (
<Image
src={profilePic}
alt="Author profile picture"
width={500}
height={500}
placeholder="blur"
/>
)
}
When you import a local image, Next.js automatically calculates the width and height, but for remote images, you must provide them manually or use the fill property.
Step 2: Handling Remote Images and Domains
I’ve found that many developers struggle when they first try to fetch images from a CMS or S3 bucket. For security, Next.js requires you to whitelist external domains in your next.config.js file. Without this, your build will fail.
// next.config.js
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'images.unsplash.com',
port: '',
pathname: '/photo/**',
},
],
},
}
module.exports = nextConfig
As shown in the image below, the correct configuration ensures that the Next.js server can proxy and optimize the remote asset before delivering it to the client.
Step 3: Implementing the ‘Fill’ Layout for Responsive Design
In modern web design, we rarely know the exact dimensions of an image. I prefer using the fill prop combined with a parent container that has position: relative. This allows the image to act like a background image while remaining fully optimized.
<div style={{ position: 'relative', width: '100%', height: '400px' }}>
<Image
src="/hero-banner.jpg"
alt="Hero Banner"
fill
style={{ objectFit: 'cover' }}
priority
/>
</div>
Note the priority prop here. This is a game-changer for LCP. By adding priority, you tell Next.js to preload this image, which is essential for any image appearing above the fold.
Step 4: Advanced Optimization with Sizes and Quality
By default, Next.js serves an optimized version of the image, but you can fine-tune this. The sizes attribute is the most underutilized tool in this next.js image component optimization tutorial. It tells the browser exactly how wide the image will be at different breakpoints, preventing the browser from downloading a 2000px image for a 300px mobile screen.
<Image
src="/product.jpg"
alt="Product"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
quality={75}
/>
I typically set quality={75} because the visual difference between 75 and 100 is negligible, but the file size difference is massive. For an even more comprehensive performance boost, you should also look into how to reduce unused javascript in next.js to ensure your scripts aren’t blocking the image render.
Pro Tips for Production
- Use AVIF: Add
images: { formats: ['image/avif', 'image/webp'] }to your config. AVIF offers significantly better compression than WebP. - Blur-up Placeholders: For local images, use
placeholder="blur". For remote images, you’ll need to provide ablurDataURL(a tiny base64 image). - Avoid excessive
priority: Only usepriorityfor the first 2-3 images on a page. Overusing it defeats the purpose of lazy loading.
Troubleshooting Common Issues
Image is distorted or stretched
This usually happens when using fill without object-fit: cover or contain in your CSS. Always define how the image should behave within its bounding box.
“Invalid src prop” Error
Double-check your remotePatterns in next.config.js. Remember that a trailing slash or a missing protocol (https) can cause this error.
What’s Next?
Now that your images are optimized, it’s time to look at the rest of your critical rendering path. I recommend auditing your bundle size and ensuring your fonts are preloaded. If you’re building a content-heavy site, consider implementing a dedicated Image CDN like Cloudinary or Imgix, which Next.js integrates with seamlessly via custom loaders.