Fallback Icons Wobbled My Sidebar: Image Dimensions and CLS
Adding fallback icons for categories without one made the sidebar jump. Explicit width/height on images, and og:image dimensions, keep CLS under control.
Deployed per-category fallback icons for the sidebar last week. Categories without a custom icon now get a default image. Opened the page, and the layout jumped: text shifted right a few pixels right after images loaded at different sizes.
I figured it was cosmetic. Annoying on a couple of screen widths, but nothing broken.
Turned out it is a CLS problem. And the fix was one line.
Browsers do not reserve space for images without dimensions
Without explicit width and height, the browser downloads the image, discovers the pixel size, then recalculates layout. That is a layout shift, and it sits squarely in Core Web Vitals territory. The CLS target is 0.1 or less at the 75th percentile, and images with unknown dimensions are one of the documented triggers [3].
The sidebar is narrow. A few pixels of text shifting in a compact sidebar is immediately visible, even if the absolute number is tiny.
I was reaching for the component to add icons, not to fix performance. Completely different mental model. <img> is what you reach for by default, but the @next/next/no-img-element lint rule flags it, because native <img> brings slower LCP and higher bandwidth compared to the Image component [1].
next/image handles size optimization and visual stability automatically. When the source is a static import, Next.js reads the intrinsic dimensions and reserves the right box. For fallback or dynamic sources, you pass width and height manually [2].
The fix was setting width and height on the component, matching the actual icon aspect ratio. No layout shift. Text stays put.
The angle shift
The original commit was purely a UI task: add icons to categories that did not have one. Not a performance fix, no mention of layout stability anywhere. But the moment those images went in at varying sizes, every category row became a potential layout shift.
That is the thing about CLS. It does not care whether you are adding a hero image or a tiny sidebar icon. If the browser does not know the size before layout, it shifts.
og:image has the same blind spot
og:image does not affect page layout, but it has the same missing-dimensions problem for a different audience. The Open Graph protocol defines og:image:width and og:image:height as structured properties, set explicitly so social preview crawlers do not have to guess [4]. og:image:alt is recommended alongside.
My take: explicit dimensions on every image, whether <img>, next/image, or <meta>, is baseline hygiene, not a performance optimization. If you are not setting width and height, you are relying on the browser to figure it out. It will, eventually. By then the user already saw the shift.
Sources
- [1] Next.js Docs: the no-img-element lint rule and why next/image is preferred
- [2] Next.js Docs: Image Optimization, intrinsic width and height, visual stability
- [3] web.dev: Cumulative Layout Shift (CLS), the 0.1 threshold at p75
- [4] Open Graph Protocol: og:image:width, og:image:height, og:image:alt