Skip to content

Responsive Images

MageObsidian ships an image helper that renders a Core-Web-Vitals-friendly <img> (or <picture>). It sets width/height to reserve layout space β€” eliminating CLS β€” and exposes loading/fetchpriority so you can defer below-the-fold images and prioritize the LCP image.

It works with any image URL, or a Vendor_Module::path asset id β€” for assets it resolves the URL and, when you omit the dimensions, auto-detects them from the source file so the markup is always CLS-safe without hand-measuring.

Scope: this is the render layer. Catalog image resizing stays with Magento (driven by view.xml); the helper wraps the resulting URL with the right attributes. It does not re-encode catalog images.


Usage

In Twig, use the image function (see Twig helpers):

1
2
3
4
5
6
7
8
{# Asset id: URL resolved, width/height auto-detected from the source file #}
{{ image('Acme_Catalog::images/banner.jpg', { alt: 'Spring sale' }) }}

{# The LCP/hero image: prioritize it (auto-eager) #}
{{ image('Acme_Catalog::images/hero.jpg', { alt: 'Hero', fetchpriority: 'high' }) }}

{# A ready URL (e.g. a catalog image): pass the known dimensions #}
{{ image(product_image_url, { alt: product.name, width: 600, height: 600 }) }}

In phtml, reach the Image ViewModel through a layout argument:

1
2
3
4
5
<block class="MageObsidian\ModernFrontend\Block\Template" name="..." template="...">
    <arguments>
        <argument name="image" xsi:type="object">MageObsidian\ModernFrontend\ViewModel\Image</argument>
    </arguments>
</block>
<?= $block->getData('image')->render('Acme_Catalog::images/banner.jpg', ['alt' => 'Spring sale']) ?>

Options

Option Default Purpose
alt '' Alternative text (always emitted for valid HTML).
width / height auto for assets Reserve layout space β€” the CLS fix.
loading lazy (eager when fetchpriority: 'high') Defer off-screen images.
decoding async Off-thread image decode.
fetchpriority β€” high for the LCP image; auto-switches loading to eager.
class, sizes, srcset β€” Standard <img> attributes.
sources β€” A list of <source>s β€” switches output to <picture>.
attributes β€” Any extra attributes (id, data-*, …).

<picture> with modern formats

Pass sources to emit a <picture> with AVIF/WebP fallbacks:

1
2
3
4
5
6
7
{{ image('Acme_Catalog::images/a.jpg', {
    alt: 'Product',
    sources: [
        { srcset: vite_url('Acme_Catalog::images/a.avif'), type: 'image/avif' },
        { srcset: vite_url('Acme_Catalog::images/a.webp'), type: 'image/webp' }
    ]
}) }}

Generating AVIF/WebP at build time is not part of this helper yet β€” provide the alternate sources yourself (or via your own pipeline). The helper assembles the <picture>; it never invents formats.

Why this helps Core Web Vitals

  • CLS: width/height (or the auto-detected intrinsic size) let the browser reserve the box before the image loads β€” no layout shift.
  • LCP: mark the hero image fetchpriority: 'high'; the helper keeps it eager so it is never lazy-loaded.
  • Everything else stays cheap: images default to loading="lazy" and decoding="async".

Next Steps