Skip to header Skip to main navigation Skip to main content Skip to footer

Main navigation

  • Home
  • Drupal Theming
  • Web Designer In Austin
  • Drupal Development Portfolio (opens in new tab)
  • Blog
  • Videos
  • Contact
Web Designer In Austin
Designing Drupal, Defining Distinction

Drupal LCP: Your Hero Image Is Lazy-Loaded by Default

Alaa Haddad, professional Drupal developer based in Austin, TX   Drupal Care
  12:16 AM CDT, Wed September 16, 2026
Share

On a Drupal site the most common Largest Contentful Paint defect is not a heavy photograph — it is that core ships every image field set to loading="lazy", and your hero banner is an image field.

Largest Contentful Paint is the moment the biggest element in the first screenful finishes rendering, and Google treats 2.5 seconds as the line between good and not. On a Drupal page that element is nearly always an image coming out of a field formatter, so the fix is usually a formatter setting rather than a server tuning exercise. If you do not yet know which layer is costing you, why your Drupal site is slow, in five layers works through them in order. This page stays inside the image pipeline, because that is where the LCP element lives.

Every file path and line number below was read out of Drupal core 11.4.6 on disk (Drupal.php line 79), not recalled.

Core lazy-loads every image field, and the default is correct

Open core/modules/image/src/Plugin/Field/FieldFormatter/ImageFormatter.php. Its defaultSettings() method returns an image_loading array whose attribute key is the string lazy, at lines 78 and 79. The responsive formatter says the same thing: ResponsiveImageFormatter.php lines 88 and 89 are identical. That value is copied straight onto the rendered tag at ImageFormatter.php line 228.

So on a stock site every image rendered through a field carries loading="lazy" until somebody changes it. For the images below the fold that is free performance and you should leave it alone. For the one image that defines your LCP it is a self-inflicted delay, because the browser will not start fetching a lazy image until layout has decided it is near the viewport — which is later than "now".

Core is explicit about the exception. The help text for the eager option, at ImageFormatter.php line 138, reads: "Force browsers to download an image as soon as possible. This is the browser default for legacy reasons. Only use this option when the image is always expected to render." A hero banner is precisely that case, and almost nothing else on the page is.

Where the switch is, and the mistake people make next to it

The setting is per field and per view mode. Go to the content type's Manage display tab, choose the view mode the hero actually renders in, open the image field's formatter settings with the gear icon, and set the loading attribute to eager. Save that view mode only.

The mistake is doing it everywhere. Set the teaser view mode eager as well and a listing page races to download twenty images at once, competing with the one you were trying to prioritise. One field, one view mode.

fetchpriority is not on that form, and core uses it exactly five times

The loading attribute decides whether the browser fetches an image early. The fetchpriority attribute decides in what order it fetches the things it has already decided to load. Core ships no field-formatter setting for it. Searching the whole of core/ for the attribute returns five occurrences and they are all the same element: the site logo, in block--system-branding-block.html.twig — the copies in the system module, stable9, claro, starterkit_theme and olivero.

If you want fetchpriority="high" on a hero image field, you add it yourself in a preprocess hook or a field template, scoped to that field and that view mode. It is the second half of the eager fix: eager says fetch this now, fetchpriority says fetch it before the other things I also just asked for. Core will not add it for you, and it will not warn you that it is missing.

Image style or responsive image style

Both ship with core and they answer different questions. Choosing the wrong one is how a 390-pixel phone ends up downloading a 1600-pixel banner.

Two core modules, two different jobs
QuestionImage styleResponsive image style
Moduleimageresponsive_image
Markup produceda single <img>a <picture> wrapping <source> elements
Who picks the fileyou, at configuration timethe browser, at request time
Loading attribute defaultlazylazy
Best forfixed-size thumbnails and teasersa banner that spans the viewport

The <picture> markup is not theoretical: it is in core/modules/responsive_image/templates/responsive-image.html.twig, which loops the source attributes and then prints the controlling image with the fallback in its srcset. A hero belongs on the responsive formatter, set to eager, every time.

The first visitor pays for the derivative

Drupal does not build image derivatives when you save the node. It builds them on the first request for that URL, in ImageStyleDownloadController::deliver() — the controller takes a lock, checks whether the file exists, and calls createDerivative() if it does not, around lines 207 to 224.

That means an LCP measurement taken right after a deployment, or right after flushing image styles, is measuring derivative generation rather than delivery. Request the page twice and record the second one. A single cold derivative reads as a regression on the first request and disappears on the next, which is a good way to spend an afternoon chasing a problem that no longer exists.

If this is already past the point where a setting fixes it, Drupal development services covers the template and preprocess side of the work.

WebP and AVIF are a core image effect, not a contrib module

Core's image module ships eight effects in src/Plugin/ImageEffect/, and two of them are format conversions: ConvertImageEffect and AvifImageEffect. The default GD toolkit lists WEBP and AVIF among its supported types in GDToolkit.php at lines 417 and 418. So you can add a convert step to an existing image style and re-serve the same field in a lighter format without installing anything.

Two cautions. AVIF support depends on the PHP build providing a working imageavif(), which core probes rather than assumes; and changing an effect invalidates every derivative that style has already generated, so the next visitor to each page pays the cost described above.

What I check first on a slow Drupal page

In order, and it rarely takes long. View source and find the LCP element. If it carries loading="lazy", that is the answer and the rest is detail. If it is eager, check whether it is an <img> from an image style at a fixed size or a <picture> from a responsive style, and compare the transferred bytes against the space the image occupies on screen. If both are sensible, the problem is no longer the image but the cache layer or the edge — long edge TTLs and safe purging is the next stop. I have been doing web development since 2005 and I author Drupal modules and themes at drupal.org/u/flashwebcenter; the lazy hero is still the single most frequent finding.

Common questions

Should I just set every image to eager?

No. That discards the benefit core is giving you and makes long pages worse. Eager belongs on the one element above the fold on first paint, in the view mode where it appears there.

Does a carousel make a good hero?

It makes a poor one. The first slide is usually the LCP element, and it sits inside a transformed container with its siblings hidden from assistive technology — when a carousel is the wrong component sets out the full cost.

Will a CDN fix my LCP?

It will shorten the trip, not the decision. A lazily-loaded hero is late everywhere, including one hop from the edge. Fix the attribute first; the CDN then makes the fixed version fast for everyone.

My LCP element is a block of text, not an image. Now what?

Then you are looking at a render-blocking stylesheet or a web font. Check how many stylesheet links the page emits and whether your font files are being preloaded. Core emits rel="preload" automatically only for fonts declared with the preload flag in a library, in HtmlResponseAttachmentsProcessor.php around lines 130 to 145 — nothing else is preloaded for you.

Where to go next

Open Manage display on your front page's content type and look at the hero field's loading attribute. It takes a minute and it is right more often than it has any business being. If it is already eager and the number still will not move, send me the URL and I will tell you which part of the pipeline is costing you, or see Drupal services for the whole chain.

LCP
Largest Contentful Paint

Footer menu

  • About
  • Privacy Policy
  • Terms & Conditions
  • Flash Web Center, LLC (opens in new tab)
  • Drupal Care (opens in new tab)
  • Log in
  • Contact

Copyright © 2026 Flash Web Center, LLC | All rights reserved

Developed & Designed by Alaa Haddad