Undeclared dimensions cause layout jump
Aliases: missing width height · unknown intrinsic size · unsized image CLS
What it is
Until an image is decoded, document flow does not know how tall a box to give it. Without width / height or aspect-ratio, the box collapses to zero or a line box, then expands to intrinsic size after decode, shoving later nodes. That is a layout jump from undeclared dimensions. What arrives late is this image’s intrinsic width and height, not an arbitrary module inserted into the tree. The late fact is “how big is this image,” not “a new block appeared.”
Why it happens
Replaced elements take their default size from the resource’s intrinsic size. Before the resource is ready the engine has no numbers, so it guesses from zero, the alt line box, or a CSS width with auto height. After decode, intrinsic size arrives and height jumps from the guess to the truth—a determinate reflow. Declaring width and height or an aspect-ratio writes intrinsic size into constraints up front; decode then fills a bitmap into an existing box. Width alone with height still auto still changes height after decode.
This is the same family of layout shift as any late content, but the attribution differs: even with no new DOM node, an <img> already in the tree jumps at decode. Lazy loading only postpones the decode instant; it does not remove the jump. Decode outside the viewport may be invisible; decode after scrolling into view hits the reading position.
Studying it
Cold-load the same page with and without width/height in markup, and see whether layout-shift points at that image node. A lab hit on disk cache finishes decode almost instantly, so the jump window is tiny and CLS reads low; on a slow uncached field network intrinsic size arrives late and the jump is complete—a classic metric mismatch. Independent variables: declared size, lazy loading, decode in or out of the viewport. Outcomes: CLS from that node, and the gap between decode completion and the shift. Do not treat a cache-hit lab session as a first-visit image.
Where it stops holding
Background images do not consume flow height, so missing dimensions will not shove neighbors, though content may float over the painting. SVG with a viewBox and a CSS width can derive height without raster pixel attributes. A fixed-height crop slot (square avatar) already locks geometry; an unsized image inside will not change the outer box. Print and prerender that wait for images show the jump before anyone sees it.
Applying it
- Put width and height or
aspect-ratioon every<img>,<video>, and embed, matching the file’s intrinsic ratio. - For responsive images, use
width/heightfor the intrinsic ratio and CSS for display width, so height follows the ratio—notwidth: 100%alone. - Lazy-loaded images still need the slot first; deferred decode is not deferred reservation.
- Verify on a cache-disabled cold load: Y coordinates of nodes below the image should match before and after decode. Field CLS entries attributed to image nodes should disappear once dimensions are declared.
Related
- Same group: R3.07.1 Size and format must be generated per use case · R3.07.3 Autoplay consumes bandwidth and attention · R3.07.4 Off-viewport media is deferred; in-viewport media is prioritized · R3.07.5 Placeholder color close to the final image reduces pop · R3.07.6 Media fallback chains must cover decode failure and unsupported formats
- Adjacent: R3.05 Render-blocking and Layout Shift · I2.01 Choosing skeleton versus spinner
- Search terms:
unsized images·layout shift·decode
Cards in the same group
- R3.07.1Size and format must be generated per use case
- R3.07.3Autoplay consumes bandwidth and attention
- R3.07.4Off-viewport media is deferred; in-viewport media is prioritized
- R3.07.5Placeholder color close to the final image reduces pop
- R3.07.6Media fallback chains must cover decode failure and unsupported formats