Pitfalls (and fixes) when lazy-loading images in Drupal

By mherchel, 20 June, 2022

As of version 9.1, Drupal automatically lazy-loads images by default. This functionality adds a loading="lazy" attribute when outputting an <img> tag. When the browser sees this, it will not download the image until it enters the viewport. This can save your user (and your web host) gobs on bandwidth by not downloading images that will never be used.

However, lazy-loading will also delay the image, which is a problem if it’s located “above the fold” — meaning that the image is in the viewport when the page loads. This will negatively impact your user’s experience, as well as slow down the Largest Contentful Paint metric within Google’s Core Web Vitals.

Now you can control when and where to lazy load images

New to Drupal 9.4 is a UI that controls the lazy loading on an image per field basis. This enables granular control over which image or media field gets lazy-loaded.

To access this UI, navigate to the “Manage display” tab of your content type, and click the “gear” icon that corresponds to your image field. This will open the field format options for the image, and there you will find a new “Image loading” details element where you can set the lazy-loading value. There are two values, “lazy” — which tells the browser to lazy-load the image, and “eager” which tells the browser to download the image as normal. “Eager” is what the browser will do if the lazy attribute is missing.

Screenshot of Drupal's Field UI options showing Image Loading options

Best practices

Because Drupal injects the loading attributes on the server, it can’t know if the image is rendered “above the fold”. This mean that we, as developers, need to make architectural decisions that will enable us to choose whether to lazy-load or not.

Drupal allows us to set the lazy-load options within an entity’s “view mode”, and we can choose when and where to render each view mode. With this knowledge in mind, you might choose to create a “hero” view mode that we know will be rendered above the fold. Or you may modify the default view mode, if you know the primary image will be rendered above the fold.

Drupal’s Views module also allows you to set whether to lazy-load or not!

Screenshot of Views UI adding an image field and showing lazy loading options

This can be useful when using attachment views that render near the top of the page.

Even more granular control

If you need even more granular control, you can set the loading attribute when preprocessing the image field

function my_module_preprocess_image(&$variables) {
  $variables['attributes']['loading'] = 'eager';

You can also modify it from within the image.html.twig template (or its variation).

<img{{ attributes.setAttribute('loading', 'eager') }} />

If you want extra control in Views, you can use a module like Views Parity Row to change view modes based on which row is rendered. This will allow you to set the first x rows to have loading="eager" and then leave the rest of the rows to default to lazy-loading.

Wrapping up

Support for lazy-loading images isn’t 100% there yet. Drupal doesn’t yet support lazy-loading of responsive images. In order to do so, the image needs to have its width and height attributes set so that there isn’t a content-shift on load. You can track this (and help out) at https://www.drupal.org/project/drupal/issues/3192234.

That being said, this new UI is a fantastic addition to Drupal core, which will enable developers to significantly speed up their sites, if they know when and how.

Hey you! Leave a comment!1

Seriously... I really like it when people let me know their thoughts and that they've read this.

The content of this field is kept private and will not be shown publicly.

James Wilson (not verified)

1 month 3 weeks ago

Thanks Mike.

Lighthouse penalizes lazy loaded images that appear above the fold with the following error:

Largest Contentful Paint image was lazily loaded

Above-the-fold images that are lazily loaded render later in the page lifecycle, which can delay the largest contentful paint.

One study found that:

Lazy loading only the images below the fold results in a complete reversal of the LCP regression and possibly even a slight improvement over disabling LCP entirely.

To fix this in Drupal 9 or 10 for a single above-the-fold image style, you can switch from lazy to eager loading try the following. My image style is called "hero".

/** * Prepares variables for image-style.html.twig * * @param array $variables * @return void */ function mytheme_preprocess_image_style(&$variables) { if ($variables['style_name'] == 'hero') { $variables['image']['#attributes']['loading'] = 'eager'; } }