Image Optimization with next/image
Why a plain img tag is a performance trap at scale, and what the Image component does about it automatically.
3 min de lectura
Unoptimized images are one of the most common causes of a slow website — a photo straight from a camera or CMS is often many times larger than it needs to be for the space it's displayed in. The <Image> component from next/image is a drop-in replacement for <img> that fixes this automatically, rather than requiring you to manually resize and compress every asset yourself.
Basic usage
import Image from "next/image";
export default function ProfileCard() {
return (
<Image
src="/profile.png"
alt="Picture of the author"
width={500}
height={500}
/>
);
}width and height aren't just styling hints — Next.js uses them to reserve the correct space for the image before it loads, which prevents the page from jumping around as images pop in. That layout shift is exactly what accessibility and performance audits flag as Cumulative Layout Shift (CLS), and it's mostly invisible until you fix it and notice how much steadier the page feels.
What "optimization" actually means here
When a visitor requests a page containing an <Image>, Next.js serves a version resized to fit the space it's actually rendered at, converted to a modern format like WebP when the browser supports it, and only loaded once it's about to enter the viewport (lazy loading, on by default for anything below the fold). None of this requires exporting multiple image sizes yourself or picking a format manually — it happens per-request, based on the requesting device.
Local images: automatic dimensions
Importing a local image file directly lets Next.js read its actual dimensions at build time, so you can skip specifying width and height by hand:
import Image from "next/image";
import ProfilePhoto from "./profile.png";
export default function ProfileCard() {
return (
<Image
src={ProfilePhoto}
alt="Picture of the author"
placeholder="blur" // uses an auto-generated low-res preview while loading
/>
);
}Remote images require explicit configuration
Because Next.js can't inspect a remote server's files during the build, width and height must be provided by hand for a remote src — and more importantly, the domain has to be explicitly allow-listed in next.config.ts:
// next.config.ts
import type { NextConfig } from "next";
const config: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.example-cms.com",
pathname: "/assets/**",
},
],
},
};
export default config;This allow-list isn't bureaucratic friction — without it, anyone could point your app's image optimizer at an arbitrary URL and use your server's bandwidth and compute to resize images for them. Being specific about the hostname and path pattern is a real security boundary, not just configuration ceremony.
When fill is the better fit
For an image that should stretch to match a parent container's size — a card's background photo, a hero banner — rather than a fixed pixel size, the fill prop positions the image absolutely inside a position: relative parent instead of requiring explicit width/height:
<div style={{ position: "relative", width: "100%", height: 300 }}>
<Image src="/banner.jpg" alt="" fill style={{ objectFit: "cover" }} />
</div>Reaching for <Image> by default, rather than <img>, costs almost nothing in code and consistently pays off in real-world page speed — it's one of the few Next.js features that's essentially free performance once you're in the habit of using it.