An Angular product grid using NgOptimizedImage logs aspect-ratio distortion and oversized-image warnings in development; what do they mean, and how do you fix them?
answer
- compares three aspect ratios
- attributes versus file versus rendered box
- height auto or fill
- file far larger than twice rendered
basics
~20 sAngular's NgOptimizedImage compares the file, the width/height attributes and the rendered box: distortion means a ratio differs by over 0.1; oversized means the file exceeds twice the rendered size by 1000px. Fix attributes, CSS or requested widths.
solid answer
~50 sIn development, after each image loads, `NgOptimizedImage` measures three things: the file's intrinsic size, the `width`/`height` you supplied, and the size the element actually renders at. If the supplied ratio differs from the file's by more than 0.1, it warns that the attributes are wrong: fix `width` and `height`. If the attributes are right but the rendered ratio differs, CSS is stretching it — typically a fixed `height` with `width: 100%`; add `height: auto`, or switch to `fill` with `object-fit: cover` if cropping is intended. The oversized warning (NG02960) fires when the downloaded file is at least 1000 px wider or taller than twice its rendered size, meaning the grid pays for pixels nobody sees: add a correct `sizes`, configure a loader so srcset can pick smaller files, or resize the source. These checks do not run in production builds.
code
ts · 19 linesimport { Component, input } from '@angular/core';
import { NgOptimizedImage } from '@angular/common';
@Component({
selector: 'app-product-tile',
imports: [NgOptimizedImage],
styles: `.thumb { position: relative; aspect-ratio: 1; }
.thumb img { object-fit: cover; }`,
template: `
<div class="thumb">
<img [ngSrc]="imagePath()" fill sizes="(max-width: 600px) 50vw, 25vw" [alt]="name()" />
</div>
<p>{{ name() }}</p>
`,
})
export class ProductTile {
readonly imagePath = input.required<string>();
readonly name = input.required<string>();
}go deeper
Know that NgOptimizedImage prints development warnings when images look distorted or are much larger than needed.
Explain the intrinsic, supplied and rendered ratios, the 0.1 tolerance, and the twice-rendered-plus-1000px oversize rule.
Work through a grid's warnings in order, fixing attributes, CSS, sizes and the loader, and use fill with object-fit for uniform crops.
Generate width and height from image metadata in the content pipeline so distortion warnings cannot recur.
## Where the warnings come from **`NgOptimizedImage`** runs a set of development-mode checks when an image loads. They are skipped in production builds, so they cost nothing there, and they are aimed at two classes of bug: images that look wrong and images that waste bytes. A product grid, with many images of mixed source sizes squeezed into CSS cards, is where both show up. ## Distortion: three aspect ratios For each non-`fill` image, the directive compares: | Ratio | Source | |---|---| | **Intrinsic** | the file's `naturalWidth / naturalHeight` | | **Supplied** | your `width / height` attributes | | **Rendered** | the element's laid-out size, minus padding | A tolerance of **0.1** absorbs sub-pixel rounding. Two different warnings (both reported as **NG02952**) can fire: 1. **Supplied vs intrinsic** differ: "the aspect ratio of the image does not match the aspect ratio indicated by the width and height attributes". The attributes are wrong — someone typed `400 × 400` for a 4:3 photo. Fix `width`/`height`. 2. **Rendered vs intrinsic** differ, with correct attributes: CSS is distorting the image. Classic cause: `width: 100%` plus a fixed `height`, or the reverse. Fixes for the second case: - Add **`height: auto`** (or `width: auto`) so CSS scales proportionally. - If the design really wants a uniform crop, switch to **`fill`** inside a positioned card with `object-fit: cover`; `fill` images are exempt from the distortion check because stretching or cropping is expected there. ## Oversized: bytes nobody sees For non-`fill` images without `ngSrcset`, the directive also compares the file's intrinsic size with the rendered size. If the file is at least **1000 px** wider (or taller) than **twice** the rendered width (or height), it warns with **NG02960**. Two times the rendered size already covers 2x screens; anything much larger is waste. Typical grid causes and fixes: - **No loader**, so no `srcset`: every card downloads the original upload. Configure `IMAGE_LOADER` or a built-in loader so the directive can request smaller widths. - **Missing `sizes`** on a responsive card: the directive builds a 1x/2x pair from the large `width` you gave. Add `sizes` that reflects the grid, such as `(max-width: 600px) 50vw, 25vw`. - **Huge sources** with no image service: resize them at build or upload time. The oversized check runs only for non-`fill` images without `ngSrcset`, and only when no distortion warning fired for that image. Moving a card to `fill` therefore silences it without saving a byte; keep a loader and a correct `sizes` so the browser actually downloads smaller files. ## Related checks you may meet in the same grid - A `fill` image whose parent has **zero height** triggers a warning: the parent is not sized or not positioned. - Invalid `loading` or `decoding` values, a `srcset` next to `ngSrcset`, or a `src` next to `ngSrc` are rejected. - Changing `width`, `height`, `sizes` or `priority` after initialisation is rejected (**NG02953**); only `ngSrc` may change later. ## A debugging order 1. Read the warning text: it prints intrinsic, supplied and rendered sizes. 2. If supplied ≠ intrinsic, fix the attributes (ideally from the image metadata, not by hand). 3. If rendered ≠ intrinsic, fix CSS (`height: auto`) or move to `fill` with `object-fit`. 4. If oversized, check that a loader exists, then that `sizes` matches the layout, then the source files. 5. Re-run in development; the warnings are the regression test.
- Why are fill images exempt from the distortion check?With `fill`, the image is meant to be stretched, cropped or letterboxed to its container through `object-fit`, and it has no `width`/`height` attributes to compare. A ratio mismatch there is by design, so the directive checks instead that the image's rendered height is not zero. The same branch also skips the oversized check, so keep `sizes` and a loader on fill images.
- Do these warnings appear in production?No. The distortion, oversized and LCP checks run only in development mode, so production pays no measuring cost. That also means they must be caught during development or in a dev-mode test run; production monitoring will not report them.
Like a picture framer who measures the print, the label on the box and the frame on the wall: if the label disagrees with the print, the box was mislabelled; if the frame disagrees with both, the frame is squashing the picture.
saying these in an interview costs you the question
- The distortion warning always means the width and height attributes are wrong.
- Setting width and height to the rendered size fixes oversized-image warnings.
- fill images are checked for distortion like fixed images.
- These checks run in production and slow the page.
- Any file larger than its rendered size triggers the oversized warning.