In a Next.js App Router app, what does the `<Image>` component from `next/image` do that a plain `<img>` tag does not, and why does it refuse to render without width and height (or `fill`)?
answer
- it still renders a real img element
- one source, many candidate URLs
- the Accept header decides the format
- dimensions are the aspect ratio, not the display size
- the optimizer is a server route
basics
~20 snext/image generates a responsive srcset, negotiates a modern format such as WebP or AVIF, lazy-loads by default, and requires intrinsic dimensions so the browser can reserve layout space before the bytes arrive, which prevents layout shift.
solid answer
~50 sA plain `<img>` downloads exactly the file you named, in the format you stored, at whatever size it happens to be, and the browser cannot reserve space for it until the response headers arrive. `next/image` renders an `<img>` too, but it fills in a `srcset` of several widths that all point at Next's optimizer route (`/_next/image?url=…&w=…&q=…`), so a phone downloads a phone-sized file. The optimizer looks at the request's `Accept` header and returns WebP or AVIF when the browser advertises support, falling back to the original format otherwise. It also sets `loading="lazy"` unless you pass `priority`, and it emits sizing styles from the width and height you gave. Those dimensions are mandatory because they encode the aspect ratio: without them Next cannot reserve the box, and the image would shove content down when it loads. `fill` is the opt-out for cases where CSS decides the box.
go deeper
Be ready to list the four concrete wins — responsive srcset, modern format, lazy by default, reserved layout space — and to say that width and height express the aspect ratio, not the on-screen size.
Explain the mechanics: the srcset candidates point at the /_next/image route, widths come from the configured size lists, and the output format is chosen from the request's Accept header rather than fixed at build time.
Show you know what the convenience costs: on-demand transforms on your server, a disk cache to manage, and an allow-list for remote hosts. Say when a plain <img> or an already-optimized asset is the better call.
Own the tradeoff of standardizing on it: it buys a consistent, hard-to-get-wrong image path across a large team, at the price of coupling delivery to your runtime and to Next's configuration surface.
## The problem `next/image` exists to solve An `<img src="/hero.jpg" alt="">` is a single instruction: fetch this exact byte stream. The browser has no idea how tall the image will be until the first bytes of the response arrive, so it lays the page out with a zero-height box and then reflows everything below it — the classic content jump. It also downloads the same 2000px JPEG on a 375px phone, and it downloads JPEG even to a browser that would happily take a much smaller AVIF. `next/image` is a wrapper that still renders a real `<img>` element, but fills in the attributes that fix each of those problems for you. ## What the component actually emits **A responsive `srcset`.** Next generates several candidate URLs, each pointing at its own optimizer endpoint: ```html <img srcset="/_next/image?url=%2Fhero.jpg&w=640&q=75 640w, /_next/image?url=%2Fhero.jpg&w=1080&q=75 1080w" src="/_next/image?url=%2Fhero.jpg&w=1080&q=75" loading="lazy" decoding="async"> ``` The widths come from the `images.deviceSizes` and `images.imageSizes` lists in `next.config` (both have sensible defaults covering common breakpoints and small fixed sizes). The browser picks one candidate using its own viewport, device pixel ratio and the `sizes` attribute — Next does not choose for it. **Format negotiation.** The optimizer reads the incoming request's `Accept` header. If the browser advertises `image/avif` or `image/webp` and that format is enabled through `images.formats`, it re-encodes and serves that; otherwise it serves the original format. This is why one `<Image>` in your source can produce AVIF for one visitor and JPEG for another. **Lazy loading.** The rendered `<img>` gets `loading="lazy"` by default, so off-screen images are not fetched until the browser decides they are near the viewport. Passing `priority` switches this off for the one image that matters for LCP. **Layout reservation.** From `width` and `height`, Next writes inline styles that lock the aspect ratio, so the box is the right shape before a single byte of the image lands. That is the whole point of the dimensions being required. **Optional blur-up.** `placeholder="blur"` renders a tiny blurred version until the real image decodes. With a static import Next generates the `blurDataURL` at build time; with a remote `src` you must supply `blurDataURL` yourself. ## Why width and height are not the rendered size Candidates often object that they cannot know the display size — it is responsive. They do not need to. `width` and `height` describe the image's *intrinsic* dimensions, and Next uses them for the aspect ratio; CSS still controls how big the element renders. If you import the file statically, you do not even type them: ```jsx import hero from './hero.png'; export default function Page() { return <Image src={hero} alt="Product hero" placeholder="blur" />; } ``` The static import resolves to an object carrying `src`, `width`, `height` and (for supported formats) `blurDataURL`, so dimensions and the blur placeholder come for free. Only remote or runtime-built URLs need explicit numbers — or `fill`, when the box is defined entirely by CSS. ## What it costs you Nothing is free. `/_next/image` is a real server route: each distinct combination of source URL, width, quality and output format is an actual image transform, cached on disk afterwards. On a managed platform that cost is bundled into the service; self-hosted, it is your CPU. And because the endpoint fetches whatever URL it is handed, remote sources must be allow-listed with `images.remotePatterns` in `next.config`, or the request is rejected. ## The honest summary for an interview `next/image` is not magic compression — it is (1) the right file size for the device, (2) the right format for the browser, (3) lazy by default, (4) space reserved up front, in exchange for a server-side transform step and a small amount of configuration. Saying only "it optimizes images" is the answer that gets follow-up questions.
- If the display size is fully responsive, what are the width and height values actually used for?They give Next the intrinsic aspect ratio, which it turns into inline sizing styles so the browser reserves a correctly shaped box before the image loads. CSS still governs the rendered size — you can set `width: 100%` and the element scales while the ratio holds. They also feed the srcset candidate generation. They are not a promise about how many pixels appear on screen.
- What does `placeholder="blur"` need in order to work with a remote image URL?An explicit `blurDataURL` prop — a small base64 data URI you generate yourself. With a static import Next produces that value at build time from the imported file, which is why blur "just works" there. For a remote `src` Next has no build-time access to the bytes, so omitting `blurDataURL` while asking for `placeholder="blur"` is an error.
- Does `next/image` do anything for an SVG by default?No — Next skips optimization for SVG unless you explicitly enable `images.dangerouslyAllowSVG` in `next.config`, and the name is a warning. SVG is executable markup, so serving attacker-supplied SVG from your own origin through the optimizer is an XSS vector. For your own icons, inline the SVG or serve it as a static file rather than routing it through the optimizer.
Think of <img> as handing every visitor the same printed poster, and next/image as a print-on-demand counter that sizes and formats a copy for whoever walks up.
saying these in an interview costs you the question
- Claims next/image compresses the file at build time
- Says width and height must equal the rendered CSS size
- Thinks it renders a custom element rather than an img
- Believes the browser gets AVIF regardless of what it supports
- Assumes optimization is free because it happens automatically