All checks
Performancemediumpoor-largest-contentful-paint

Real visitors wait too long to see your main content

Largest Contentful Paint measures how long it takes for the biggest thing on screen — usually your hero image or headline — to actually appear.

3% · n=31 · as of 15 September 2026 of sites ShipReady scanned had this, counted across sites where this check completed.

Why it matters

Largest Contentful Paint measures how long it takes for the biggest thing on screen — usually your hero image or headline — to actually appear. Google's own threshold for "poor" is 4 seconds, and Chrome recorded the observed value at the 75th percentile for your site: a measured share of real visits were in the poor band. This is not a lab estimate. It is what happened to people who visited your site over the last 28 days, on their real devices and connections. A quarter of them had it worse than the observed value. It is also a confirmed Google ranking signal, and the point at which visitors start leaving before the page has drawn.

How ShipReady detects it

Core Web Vitals, from the Chrome UX Report. ──────────────────────────────────────────────────────────────────────────── WHY THIS CHECK CAN EXIST AT ALL ──────────────────────────────────────────────────────────────────────────── checks/performance.py ends with a deferral note: the real Core Web Vitals — LCP, INP, CLS — are measured in a rendering browser, so a passive scanner cannot produce them, and that pillar ships only observable proxies ("images have no dimensions, which can cause layout shift", never "your CLS is 0.3"). CrUX changes the premise rather than the constraint. It is Google's public dataset of what REAL Chrome users actually experienced on a site over the trailing 28 days. We are not measuring anything and not estimating anything — we ask Google what it recorded and quote the answer. The scanner still renders nothing. That also makes this the strongest evidence in the whole scanner. Every other rule reasons from what a site COULD do to a visitor; this one reports what it DID do, to real people, in the field. ──────────────────────────────────────────────────────────────────────────── THE THREE OUTCOMES, WHICH MUST NEVER COLLAPSE INTO EACH OTHER ──────────────────────────────────────────────────────────────────────────── 1. DATA, AND IT IS POOR -> a finding per metric in the poor band. 2. DATA, AND IT IS FINE -> silence. We looked, and there is nothing to say. This is a real pass. 3. NO DATA -> ScanSignals.note_incomplete, and NO finding. Outcome 3 is the one that matters. CrUX only publishes an origin once it has enough Chrome traffic to be statistically meaningful and to preserve anonymity, so a new site, a low-traffic site, or an internal tool has no record at all — and that is the COMMON case for this product's users, who are scanning something they just launched. Returning silence for both 2 and 3 would report "Performance: no issues" for a site nobody has ever measured. That is the exact failure the coverage-caveat system exists to prevent, so no-data goes through it and the report says the metrics were not assessed. ──────────────────────────────────────────────────────────────────────────── WHY ONLY THE "POOR" BAND ──────────────────────────────────────────────────────────────────────────── Google defines three bands per metric. This reports only POOR, never "needs improvement" — a deliberate false negative, in the direction this scanner always errs. A site at LCP 3.9s is genuinely not great and will not be told so here; a site at LCP 4.6s is failing real users and will be. Reporting the middle band would roughly triple the finding count for advice that reads as "could be better", which is not what a production-readiness verdict is for. ──────────────────────────────────────────────────────────────────────────── WHAT THIS SENDS, AND WHERE ──────────────────────────────────────────────────────────────────────────── One POST per scan, to Google, containing the scanned ORIGIN and nothing else. No page URLs, no crawl contents, no visitor data — and nothing is sent to the customer's own site, so this check cannot contribute to bot mitigation. Queried by ORIGIN rather than by URL on purpose. Origin-level records are aggregated across the whole site, so they exist for far more sites than per-URL records do, and the answer describes the site rather than whichever page the crawler happened to seed on. REQUIRES `CRUX_API_KEY`. Without one this check is INERT: it returns nothing and records no caveat, because a key we have not deployed is a gap in our product, not a fact about the customer's site. Telling every reader that Core Web Vitals "were not assessed" because of our own configuration would put an unactionable caveat on 100% of reports. It logs `crux_api_key_missing` instead.

Detection is deterministic. ShipReady reports this only when it observes the condition directly, and prefers to miss a real problem over inventing one. Rule version 1.0.0.

How to fix it

This is the prompt ShipReady puts in your report — written to be pasted straight into Cursor, Claude Code, or whichever assistant built the app.

Real Chrome users are waiting past Google's "poor" threshold of 4 seconds for your main content to appear. Find what the largest element actually is before changing anything: open DevTools → Performance, reload, and look at the LCP marker — it names the element. Optimising the wrong image is the usual wasted afternoon.

IF IT IS AN IMAGE, which it usually is. Give it `fetchpriority="high"` and make sure it is NOT lazy-loaded — `loading="lazy"` on the hero image is the single most common cause of a bad LCP, because the browser deliberately waits before fetching it. Serve it as AVIF or WebP at the size it is actually displayed, and preload it: `<link rel="preload" as="image" href="/hero.avif" fetchpriority="high">`. In Next.js, `next/image` with `priority` does all of this.

IF IT IS TEXT, the delay is usually the font. Add `font-display: swap` so text paints immediately in a fallback, preload the font file, and self-host it rather than pulling it from a third party — a Google Fonts stylesheet costs an extra DNS lookup, connection and round trip before the font is even requested.

IF THE PAGE IS CLIENT-RENDERED, no image work will fix this: the browser cannot paint until your JavaScript has downloaded, parsed and run. Server-render or statically generate the part of the page that holds the main content.

THEN CHECK THE SERVER. If your Time To First Byte is over ~800ms nothing downstream can save the metric. Cache the HTML at the edge, and move slow database or API calls out of the critical render path.

Re-measure in the field, not in the lab. This figure comes from real visitors over 28 days, so it will lag your fix by weeks — use PageSpeed Insights for the lab number immediately, and expect the field number to follow.

Frequently asked questions

What does "Real visitors wait too long to see your main content" mean?
Largest Contentful Paint measures how long it takes for the biggest thing on screen — usually your hero image or headline — to actually appear.
How serious is it?
ShipReady rates this medium. Fix soon. Meaningfully weakens a defence or degrades how the site works.
How common is this?
Across the sites ShipReady has scanned where this check completed, 3% · n=31 · as of 15 September 2026.
How do I fix it?
Paste the fix prompt on this page into Cursor, Claude Code or your AI editor. It is the same prompt ShipReady puts in your report.
Can I check my own site?
Yes — ShipReady scans up to ten pages of any public site for free and reports this alongside every other check. The free report lists every issue it finds and shows full evidence and a fix prompt for the critical and high-severity ones; medium and low findings are counted and unlock on Pro.

Related checks

Run this check on your site

ShipReady checks this and 193 other things across up to ten pages of your site, with an AI-ready fix for each. Free, no signup.