In next/image, when do you reach for the `fill` prop instead of width and height, what must be true of the parent element, and why does a `fill` image usually also need a `sizes` prop?
answer
- absolutely positioned, so the parent owns the box
- nearest positioned ancestor, with a real height
- no layout width means no good candidate choice
- the silent fallback is full viewport width
- object-fit decides crop versus letterbox
basics
~20 sUse fill when the intrinsic dimensions are unknown; it absolutely positions the image to fill the nearest positioned ancestor, which must have position: relative and a real height. sizes then tells the browser which srcset candidate to download.
solid answer
~50 s`fill` is for the case where you do not know — or do not care about — the image's intrinsic size, typically a CMS-driven photo dropped into a fixed card or a hero band. Instead of computing an aspect-ratio box, Next absolutely positions the `<img>` to cover its container, so the container has to be a positioned element (`position: relative`, `absolute` or `fixed`) with a height of its own; otherwise the image collapses to nothing. You then control cropping with `object-fit` in `style` or a class. The catch is that an absolutely positioned image gives the browser no layout width to reason about, so without `sizes` Next falls back to `sizes="100vw"` — the browser assumes full viewport width and pulls a candidate far larger than a 300px card needs. Passing an honest `sizes` such as `"(max-width: 768px) 100vw, 300px"` is what makes the responsive srcset actually save bytes.
code
jsx · 15 linesimport Image from 'next/image';
export function Thumbnail({ photo }) {
return (
<div style={{ position: 'relative', width: 300, height: 200, overflow: 'hidden' }}>
<Image
src={photo.url}
alt={photo.alt}
fill
sizes="(max-width: 640px) 100vw, 300px"
style={{ objectFit: 'cover' }}
/>
</div>
);
}go deeper
Remember the two mutually exclusive ways to size a next/image: intrinsic width and height, or fill inside a parent you have positioned and given a height.
Explain why fill changes the layout contract — the image goes out of flow, the parent must supply the box, object-fit decides cropping, and sizes replaces the layout width the browser no longer has.
Show you audit sizes values against the real CSS at each breakpoint, and can point to the oversized-download symptom that a copy-pasted 100vw produces on a card grid.
Own the convention: decide where in your component library fill is allowed at all, and make the sizes string a required prop of the wrapper so product teams cannot silently ship full-viewport downloads.
## Two ways to tell Next how big the box is `next/image` must reserve layout space before the file arrives. There are exactly two ways to give it what it needs: 1. **`width` and `height`** — you state the intrinsic dimensions, Next derives the aspect ratio and emits sizing styles. The element keeps its ratio while CSS scales it. 2. **`fill`** — you state nothing, and Next absolutely positions the image to fill whatever box its parent already defines. ```jsx <div style={{ position: 'relative', height: 200 }}> <Image src={photo.url} alt={photo.alt} fill sizes="(max-width: 768px) 100vw, 300px" style={{ objectFit: 'cover' }} /> </div> ``` ## When `fill` is the right choice Reach for it when the *source* dimensions are unknown at author time but the *box* is known from CSS: user-uploaded or CMS images, avatars in a fixed circle, cards in a grid, a hero band that is always 40vh. Reach for `width`/`height` when you do know the intrinsic size — especially for a static import, where Next reads the dimensions from the file for you and you should not be using `fill` at all. ## The parent contract Because the image is absolutely positioned, it is laid out against its **nearest positioned ancestor**. Two things must hold: - that ancestor is positioned (`relative` is the usual choice); - that ancestor has a height that does not depend on the image. The second one is where the common bug lives. An absolutely positioned child contributes nothing to its parent's height, so a `<div style={{ position: 'relative' }}>` with no height wrapping a `fill` image renders a zero-height container and an invisible image. If the parent is not positioned at all, the image escapes further up the tree and stretches across some ancestor you did not intend — usually the whole page. Since the image no longer has an aspect-ratio box, it will stretch to the container's shape unless you say otherwise. `object-fit: cover` (crop to fill) or `object-fit: contain` (letterbox) is effectively part of the pattern, along with `overflow: hidden` on the parent if you are cropping. ## Why `sizes` matters more here than anywhere else The `srcset` Next generates is a menu of widths. The browser picks from that menu using the layout width of the element, its device pixel ratio, and the `sizes` attribute. With `width`/`height` and no `sizes`, Next can emit a fixed-size srcset (1x/2x) around the declared width, which is usually fine. With `fill`, there is no declared width, and the element is out of flow, so the browser has to choose a candidate *before* layout tells it anything useful. Next's fallback is `sizes="100vw"`, which is a claim that the image spans the whole viewport. On a 1440px desktop with a 2x screen, that pushes the browser toward the largest candidate available — for an image that renders in a 300px card. Users pay in bytes and in decode time, and the page often gets a worse LCP than a plain `<img>` would have. A correct `sizes` is just a media-query list describing the element's rendered width per breakpoint: ```jsx sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 300px" ``` Read it as: below 640px this image is the full viewport width; below 1024px it is half; otherwise it is 300 CSS pixels. Providing `sizes` also tells Next to generate candidates across the full configured width range rather than a narrow fixed-size pair, so the browser has genuinely useful options to choose from. ## How this goes wrong in review - `sizes="100vw"` copy-pasted onto a thumbnail grid. It is syntactically fine and silently downloads the largest variant for every tile. - `fill` used on a statically imported asset whose dimensions were already known — you gave up the free aspect ratio for nothing. - A parent with `position: relative` but height driven by its (absolutely positioned) child, producing an empty box. - `sizes` describing the intended design rather than what the CSS actually does; it is a hint the browser trusts, and a wrong hint is worse than a rough-but-honest one. The one-line rule: `fill` delegates the box to CSS, and once CSS owns the box, `sizes` is how you tell the browser what CSS decided.
- What visually happens when the parent of a `fill` image has `position: relative` but no height?The container renders at zero height and the image is invisible. An absolutely positioned child is out of flow, so it contributes nothing to its parent's height — the parent must get its height from something else: a fixed value, an aspect-ratio rule, padding, or a grid or flex track. This is the single most common `fill` bug and it produces no console error.
- Is `sizes` a promise the browser enforces, or a hint?A hint, and the browser trusts it. It uses `sizes` together with device pixel ratio to pick a srcset candidate before layout is final, so a wrong value is not corrected later — an inflated `sizes` just means an oversized download, and an understated one means a blurry image. Describe what your CSS actually does at each breakpoint rather than what you wish it did.
- Would you use `fill` for a statically imported logo?No. A static import already gives Next the intrinsic width and height, so plain `width`/`height` (or the bare import with no dimensions) reserves the correct aspect-ratio box with no CSS contract to honour. `fill` would throw away that information and add a positioned-parent requirement for nothing.
saying these in an interview costs you the question
- Thinks fill means the image scales itself to its content
- Omits sizes and assumes the srcset still saves bytes
- Uses fill without positioning or sizing the parent
- Believes object-fit is applied automatically by fill
- Sets sizes to 100vw on every image as a safe default