You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix product card and CMS image sizing to prevent duplicate/oversized image requests. Move fixed dimensions and `densities="1x"` into the `productCard` preset, and use `useElementSize`-based `width`/`height` props for `CmsElementImage`.
Copy file name to clipboardExpand all lines: packages/cms-base-layer/AGENTS.md
+40-5Lines changed: 40 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -395,12 +395,47 @@ Set `format` or `quality` to `undefined` to omit that parameter. Requires remote
395
395
396
396
## Responsive Image Architecture
397
397
398
-
CMS images use a provide/inject pattern for responsive sizing:
398
+
### Product Card Images (`SwProductCardImage`)
399
399
400
-
1.**`CmsGenericBlock`** counts slots, calls `provide("cms-block-slot-count", slotCount)` and `provide("cms-image-sizes", getImageSizes(slotCount, appConfig.imageSizes))`
401
-
2.**`CmsElementImage`** calls `inject("cms-image-sizes", "100vw")` and passes it to `<NuxtImg :sizes="...">`
402
-
3. If media has thumbnails → native `srcset` from `getSrcSetForMedia()`. If not → synthetic `srcset` via `generateCdnSrcSet()` from `@shopware/helpers`
403
-
4.**`useLcpImagePreload`** scans CMS sections for the first image and injects `<link rel="preload" as="image" fetchpriority="high">` during SSR
400
+
The `productCard` preset only defines URL modifiers (format/quality/fit). `width`/`height`/`densities`/`loading` must stay on the component — NuxtImg presets don't propagate these reliably:
> **Note:** Avoid adding `decoding` or `sizes` props on the component — they've caused Vue hydration attribute mismatches with NuxtImg, which trigger duplicate image requests.
422
+
423
+
### CMS Images (`CmsElementImage`)
424
+
425
+
CMS images use `useElementSize()` to measure the rendered container and pass the size to NuxtImg via `width`/`height` props:
Copy file name to clipboardExpand all lines: packages/cms-base-layer/README.md
+28-48Lines changed: 28 additions & 48 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -372,68 +372,48 @@ The preload URL includes the optimized `format` and `quality` parameters from `a
372
372
373
373
## Responsive CMS Images
374
374
375
-
CMS image elements (`CmsElementImage`) automatically serve appropriately-sized images using responsive `srcset` and `sizes` attributes. This prevents the browser from downloading images larger than their displayed dimensions — a common Lighthouse performance issue.
375
+
Images are optimized to prevent the browser from downloading images larger than their displayed dimensions — a common Lighthouse performance issue.
376
376
377
-
### How it works
378
-
379
-
1.**`CmsGenericBlock`** counts the number of slots in each block, `provide`s a responsive `sizes` value (e.g., a 2-slot block means images are ~50% viewport width on desktop), and `provide`s the slot count via `cms-block-slot-count` for slider SSR breakpoint scaling.
380
-
2.**`CmsElementImage`**`inject`s the sizes hint and applies it to `<NuxtImg>`.
381
-
3. If the media has **pre-generated thumbnails** from Shopware, the existing `srcset` from thumbnails is used.
382
-
4. If **no thumbnails** exist, a synthetic `srcset` is generated using CDN width-based resizing (`?width=400`, `?width=800`, etc.) via the `generateCdnSrcSet` helper from `@shopware/helpers`.
383
-
5.**Slider components** (`CmsElementProductSlider`, `CmsElementCrossSelling`) `inject` the slot count to scale their SSR breakpoints — ensuring media queries account for the container being a fraction of the viewport (e.g., a 2-slot block doubles the breakpoint thresholds).
384
-
385
-
The browser combines `sizes` + `srcset` to download only the image size it actually needs — during HTML parsing, before any JavaScript runs.
386
-
387
-
### Configuration
377
+
### Product Card Images (`SwProductCardImage`)
388
378
389
-
Default slot-count-to-sizes mappings are set in `app.config.ts` and can be overridden:
379
+
The `productCard` preset only defines URL modifiers (format/quality/fit). `width`/`height`/`densities`/`loading` stay on the component — NuxtImg presets don't propagate these reliably:
Individual block components can override the sizes value by calling `provide("cms-image-sizes", "custom value")` — this takes precedence over the default from `CmsGenericBlock`.
400
+
> **⚠️ Avoid** adding `decoding` or `sizes` props on the component — they trigger Vue hydration attribute mismatches with NuxtImg, which cause duplicate image requests.
419
401
420
-
### Synthetic srcset fallback
402
+
### CMS Images (`CmsElementImage`)
421
403
422
-
When Shopware media has no thumbnails (common in Cloud/SaaS setups using CDN-based resizing), the layer generates a synthetic `srcset` using CDN query parameters:
404
+
CMS image elements use `useElementSize()` to measure the rendered container and pass the size to `<NuxtImg>` via `width`/`height` props:
- During SSR, no image is fetched (size is `undefined`)
407
+
- After hydration, the container is measured and a single correctly-sized image is requested
408
+
- The size is multiplied by 2 (for retina) and rounded up to the nearest 100px
409
+
410
+
### Slider Components
411
+
412
+
**Slider components** (`CmsElementProductSlider`, `CmsElementCrossSelling`) `inject` the slot count via `cms-block-slot-count` to scale their SSR breakpoints — ensuring media queries account for the container being a fraction of the viewport.
433
413
434
-
The `format` and `quality` values are taken from the `backgroundImage` config in `app.config.ts`.
414
+
### LCP Image Preloading
435
415
436
-
> **Note:**Synthetic srcset requires CDN-based image resizing support. See the [Image Optimization](#%EF%B8%8F-image-optimization) section for requirements.
416
+
**`useLcpImagePreload`**scans CMS sections for the first image and injects `<link rel="preload" as="image" fetchpriority="high">` during SSR.
0 commit comments