Next.js Image component features most developers skip over
I used the Next.js Image component for two years before realizing I was using maybe 30% of its capabilities. I knew it optimized format (WebP/AVIF) and lazy loaded by default. But placeholder blurs, art direction, fill mode, and the loader API were all sitting there unused. Here is what each feature does and when to reach for it.
Placeholder blur: instant visual feedback
For locally imported images, Next.js generates a base64 blur placeholder at build time — zero runtime cost:
import Image from 'next/image';
import heroPhoto from '@/public/hero.jpg'; // Local import
// Automatically generates blur placeholder from the imported image
export function HeroSection() {
return (
);
}
For remote images, provide your own blur data URL:
// Generate blur placeholder from URL at request time (e.g., in Server Component)
import { getPlaiceholder } from 'plaiceholder';
async function ProductImage({ imageUrl }: { imageUrl: string }) {
const { base64 } = await getPlaiceholder(imageUrl);
return (
);
}
fill mode: responsive images in fluid containers
// When you don't know the dimensions ahead of time
// fill makes the image expand to fill the parent
function ProductCard({ product }: { product: Product }) {
return (
{/* Parent MUST have position: relative and explicit dimensions */}
{product.name}
);
}
sizes: the most impactful prop nobody sets
The sizes prop tells the browser how wide the image will be at different viewport sizes. Without it, the browser downloads a desktop-sized image on mobile. With it, it downloads the right size:
// Full-width hero — full viewport width at all sizes
// Two-column grid — full width on mobile, half on desktop
// Three-column grid — full/half/third based on breakpoint
// Sidebar image — always narrow
priority: load above-the-fold images eagerly
// Without priority: lazy loaded, contributes to LCP delay
// With priority: preloaded, eliminates LCP delay for above-fold images
// Use on the FIRST visible image on each page
to the document head
sizes="100vw"
fill
/>
Custom loader for image CDNs
// next.config.ts — configure your image CDN
const nextConfig = {
images: {
loader: 'custom',
loaderFile: './src/lib/imageLoader.ts',
},
};
// src/lib/imageLoader.ts
export default function cloudinaryLoader({
src,
width,
quality,
}: {
src: string;
width: number;
quality?: number;
}) {
const params = [
'f_auto',
'c_limit',
`w_${width}`,
`q_${quality || 'auto'}`,
];
return `https://res.cloudinary.com/your-cloud/image/upload/${params.join(',')}/${src}`;
}
The LCP improvement from doing this right
On a product listing page with 12 images, before optimizing:
- LCP: 4.2s (mobile 3G)
- Total image bytes: 2.4MB
After adding sizes, priority on the hero, and placeholder="blur":
- LCP: 1.1s (mobile 3G)
- Total image bytes: 380KB
The LCP improvement came almost entirely from the sizes prop. Mobile devices were downloading desktop-optimized images at 4x the required size. Setting sizes correctly is the single highest-ROI image optimization you can make.