squirrelscan
GitHub

LCP image not preloaded: what squirrel counts as a hero

Likely Largest Contentful Paint images loaded with no preload. squirrel checks the first three eligible candidate images on every page.

What LCP preloading is

Largest Contentful Paint measures when the biggest element in the viewport finishes painting. On most pages that element is an image, and the browser cannot start fetching it until it has parsed the markup that references it. A <link rel="preload" as="image"> in <head> starts the fetch earlier, before the parser reaches the <img>.

Preloading helps when the image is discoverable late, which is the usual case behind a slow LCP: a hero inside a component, an image referenced from CSS, or one whose URL is only known after a script runs. It does not help an image the parser reaches immediately anyway, and preloading several images at once slows the one that matters.

What squirrel checks

The rule runs on every crawled page and emits one check:

  • lcp-preload, warning with the message N likely-LCP images loaded without preload, or 1 likely-LCP image loaded without preload in the singular. The value is the count. There are no items: the count is per page rather than a list of every image across the site.
  • lcp-preload, passing with the message LCP images appear to be optimized.

A candidate is one of the first three eligible images in document order. This is the same eligibility perf/lcp-fetchpriority uses, from one shared finder, so the two rules pick the same hero image. An image is eligible when it has a src that is not a data: URI, and it:

  • is not lazy (loading="lazy");
  • is not an SVG or ICO, and not a tracking pixel or spacer (pixel, spacer, blank, 1x1 or tracking in the src);
  • is not inside a <header> or <nav>, where logos live;
  • is not declared small: when both width and height are declared, the larger must be at least 300. An image with no declared dimensions stays eligible.

A candidate counts as unpreloaded when no link[rel~="preload"][as="image"] on the page matches it. A preload matches when its href or any imagesrcset URL equals the image’s src or any of its own srcset URLs. URLs are compared as absolute URLs without the query string or fragment, so a relative src, an absolute preload href and a cache-busting ?v=2 all match. A preload for some other image is no evidence about this one. The count is not capped at one: two eligible images that are both unpreloaded report 2 likely-LCP images.

The candidates come from a shared analysis of the page that six performance rules read, computed once per page.

Elements inside <noscript> are skipped: a browser with scripting enabled reads <noscript> content as plain text, so nothing in it loads or renders. See content inside noscript.

How to fix it

<link rel="preload" as="image" href="/hero.jpg" fetchpriority="high" />

Put it in <head>, for one image, and only when that image really is the largest element in the viewport.

The common false positives are a page whose LCP element is text or a CSS background, neither of which this rule can see, and a hero whose preload names a different file than the src and srcset it renders. A preload written relative or absolute, with or without a query string, or through imagesrcset, matches.

Rule IDperf/lcp-hints
CategoryPerformance
ScopePer-page
Severitywarning
Weight7/10

Enable / disable

Disable this rule

[rules]
disable = ["perf/lcp-hints"]

Disable all Performance rules

[rules]
disable = ["perf/*"]

Enable only this rule

[rules]
enable = ["perf/lcp-hints"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Performance findings ship in every audit next to the SEO, security and agent experience rules. See Core Web Vitals for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Performance section of the report. Each page reports how many likely-LCP images went unpreloaded. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close