Slow-loading images are still one of the biggest reasons websites fail their Core Web Vitals. The good news? If you’re using Next.js, you already have one of the most powerful image optimization tools available right out of the box. In this practical tutorial, we’ll show you exactly how to lazy load images in a Next.js application using the built-in next/image component, configure remote sources, add elegant blur placeholders, and measure the real performance gains with Lighthouse.
What Does Lazy Loading Actually Do?
Lazy loading defers the loading of images until they are about to enter the user’s viewport. Instead of downloading every image on page load (even images below the fold), the browser only fetches them when needed. The result:
- Faster initial page load and improved Largest Contentful Paint (LCP)
- Reduced bandwidth consumption for users on mobile or slow connections
- Better Core Web Vitals scores, which directly impacts SEO
- Improved user experience, especially on image-heavy pages

Does Next.js Lazy Load Images Automatically?
Yes. This is the key thing most developers miss: when you use the next/image component, lazy loading is enabled by default. You don’t need to add any extra prop or library. The component uses native browser lazy loading combined with intelligent loading distance calculation.
Step 1: Setting Up the next/image Component
Let’s start with the simplest example. In any Next.js page or component, import the Image component:
import Image from 'next/image';
import heroImage from '../public/hero.jpg';
export default function HomePage() {
return (
<Image
src={heroImage}
alt="Hero banner"
width={1200}
height={600}
/>
);
}
That’s it. This image is now lazy loaded by default. The browser will only download it when the user scrolls close enough to the viewport.
Understanding the loading Prop
The Image component accepts a loading prop with two possible values:
| Value | Behavior | When to Use |
|---|---|---|
| lazy (default) | Defers loading until image is near the viewport | Below-the-fold images, galleries, long pages |
| eager | Loads immediately regardless of position | Hero images, LCP candidates (use with priority) |
Important: For your hero image or any image that is the LCP element, use the priority prop instead. This tells Next.js to preload that specific image.
<Image
src={heroImage}
alt="Hero banner"
width={1200}
height={600}
priority
/>
Step 2: Configuring Remote Images
If your images come from a CDN, a CMS, or any external source, you need to whitelist the domains in your next.config.js file. Since Next.js 14, the recommended approach uses remotePatterns:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.dailygit.com',
port: '',
pathname: '/uploads/**',
},
{
protocol: 'https',
hostname: 'images.unsplash.com',
},
],
},
};
module.exports = nextConfig;
Now you can safely use remote images, and they’ll still benefit from lazy loading and optimization:
<Image
src="https://cdn.dailygit.com/uploads/article-cover.jpg"
alt="Article cover"
width={800}
height={450}
/>
Common Remote Image Mistakes
- Forgetting to define
widthandheight(required for remote sources unless usingfill) - Using the deprecated
domainsarray instead ofremotePatterns - Not specifying the
protocol, which defaults to none and fails silently

Step 3: Adding Blur Placeholders for a Smoother UX
One of the best features of next/image is the ability to show a blurred preview while the actual image loads. This eliminates that ugly empty space and makes loading feel instant.
For Local Images (Automatic Blur)
If you import the image statically, Next.js generates the blur placeholder automatically:
import productImage from '../public/product.jpg';
<Image
src={productImage}
alt="Product"
placeholder="blur"
/>
For Remote Images (Manual blurDataURL)
For remote images, you need to provide a blurDataURL, which is a tiny base64-encoded version of the image:
<Image
src="https://cdn.dailygit.com/uploads/photo.jpg"
alt="Photo"
width={800}
height={600}
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABA..."
/>
You can generate these base64 strings using tools like plaiceholder or sharp at build time, or directly from your CMS if it supports it.
Step 4: Measuring Performance with Lighthouse
Now the fun part: proving that lazy loading actually works. Here’s how to benchmark your improvements properly.
Before You Test
- Build your app for production:
npm run build && npm run start - Open Chrome in Incognito mode to avoid extension interference
- Open DevTools (F12) and navigate to the Lighthouse tab
- Select Performance and Mobile for the most realistic results
- Click Analyze page load
What to Look At
| Metric | Target | Why It Matters |
|---|---|---|
| LCP | < 2.5s | Measures when the largest element renders |
| Total Blocking Time | < 200ms | Lower with fewer image requests upfront |
| CLS | < 0.1 | next/image prevents layout shift via dimensions |
| Total Page Weight | As low as possible | Bandwidth saved by deferred images |
Typical Results We See
On image-heavy pages (think product galleries or blog archives), switching from regular <img> tags to lazy-loaded next/image components typically yields:
- 40 to 70% reduction in initial page weight
- 1 to 3 seconds faster LCP on mobile 4G connections
- Lighthouse Performance scores jumping from the 60s into the 90s
Bonus: Common Pitfalls to Avoid
- Don’t use priority on every image. It defeats the purpose of lazy loading. Reserve it for the LCP image only.
- Don’t forget the alt attribute. It’s required for accessibility and SEO.
- Don’t mix loading=”lazy” with priority. They contradict each other.
- Use the sizes prop with responsive images. It helps the browser pick the right resolution.
- Don’t lazy load above-the-fold images. Lazy loading them can actually hurt LCP.

Alternative: Lazy Loading Without next/image
If for some reason you can’t use the Image component (for example, you’re using SVG sprites or need a feature it doesn’t support), the native HTML loading="lazy" attribute still works:
<img src="/photo.jpg" alt="Photo" loading="lazy" width="800" height="600" />
However, you’ll lose automatic format conversion (WebP/AVIF), responsive sizing, and blur placeholders. We strongly recommend sticking with next/image whenever possible.
Final Thoughts
Lazy loading images in Next.js isn’t just a nice-to-have anymore. With Core Web Vitals being a confirmed ranking signal, properly optimized images can be the difference between page 1 and page 5 of Google. The next/image component gives you lazy loading, format optimization, responsive sizing, and blur placeholders out of the box, with minimal configuration.
Implement it today, run Lighthouse, and watch your scores climb.
FAQ
Does Next.js lazy load images by default?
Yes. When you use the next/image component, lazy loading is enabled by default through the loading="lazy" behavior. You only need to override this if you want eager loading or to use the priority prop for LCP images.
Should I lazy load all my images?
No. You should never lazy load images that appear above the fold, especially your hero image or LCP candidate. Lazy loading those can actually hurt performance. Use the priority prop on those instead.
How do I lazy load images in Next.js without the Image component?
You can use the native HTML attribute: <img src="..." loading="lazy" />. However, this approach loses all the optimization benefits of next/image, including format conversion, responsive sizing, and automatic blur placeholders.
What is the difference between placeholder=”blur” and a regular spinner?
The blur placeholder shows a low-quality, blurred version of the actual image while the full version loads. This feels much more natural to users than a spinner because the layout is preserved and the visual transition is smooth, reducing perceived loading time.
Why aren’t my remote images loading in Next.js?
The most common reason is that you haven’t whitelisted the domain in next.config.js under images.remotePatterns. Next.js blocks external image sources by default for security reasons.
Does lazy loading affect SEO?
Positively, in most cases. Faster load times and better Core Web Vitals scores can improve rankings. Just make sure your images have proper alt attributes and that critical above-the-fold images are not lazy loaded.
