How to Fix LCP on Mobile: Split the Number Before You Fix Anything
Largest Contentful Paint is not one number. It is four delays stacked end to end, and until you split them you are guessing.
We opened Lighthouse on a client site last year and the mobile run came back with an LCP of 7.3 seconds and a performance score of 61. That is a real trace of one page, on one throttled run, in one lab. It is not a measurement of what that client's actual visitors experienced, and we want to be precise about that distinction up front, because most LCP advice on the internet is a fix presented without a diagnosis.
For scale: in our 292-site study, 191 sites returned a valid mobile verdict and the median mobile LCP was 10.5 seconds. 165 of those 191 were over 4 seconds. A 7.3 second LCP is not an outlier. It is the middle of the pack.
What LCP actually measures
LCP is the time from navigation start to the moment the largest visible element in the viewport finishes rendering. Usually that is a hero image, an H1, or a block of body text. Google's published thresholds are under 2.5 seconds (good), 2.5 to 4.0 seconds (needs improvement), and over 4.0 seconds (poor).
Two corrections to what this blog has said before. First, Google's thresholds are defined against field data at the 75th percentile of real page loads. Lighthouse gives you a lab number from a single simulated load. They are related, not interchangeable. Second, an earlier version of this post said that anything over 4 seconds is "actively hurting your rankings." That was an overstatement. Google has confirmed that page experience signals including Core Web Vitals are used in ranking, and Google has repeatedly said they are a small input that does not override relevance. Fix LCP because a seven second blank screen loses people, not because you have been promised a rankings jump.
The only diagnostic that matters: split the number
Google breaks LCP into four sub-parts. Every real fix belongs to exactly one of them. If you cannot say which part of the number your change is supposed to move, do not ship the change.
- Time to first byte. How long the server takes to start responding. This is hosting, redirects, cold starts, and server-side rendering work.
- Resource load delay. The gap between the first byte arriving and the browser starting to fetch the LCP resource. This is a discovery problem. The browser could not find out about the image until it parsed something else first.
- Resource load duration. How long the LCP resource takes to download once the fetch begins. This is bytes, format, and connection.
- Element render delay. The gap between the resource finishing and the element actually painting. This is blocked rendering, hydration, and animation.
Chrome DevTools gives you this breakdown directly. Open the Performance panel, enable mobile CPU and network throttling, record a load, and click the LCP marker in the Timings track. It names the LCP element and shows the phase breakdown. The Chrome web-vitals library reports the same four attribution fields in the field.
Google itself warns against assuming the answer is image compression. On plenty of pages the LCP element is text, or the image is already small and the delay is entirely server time and render blocking. Compressing a hero image that was never the bottleneck buys you nothing and costs you a day.
What each part looked like on the 7.3 second page
Here is the honest version of the trace. We did not ship these four changes one at a time with a clean measurement between each, so we cannot attribute a specific number of seconds to any single fix. Anyone who hands you a per-fix breakdown from a single Lighthouse pass is reporting run-to-run variance as science. What we can say is which phase each problem lived in.
Resource load delay: the hero image was lazy loaded. In Next.js, images are lazy by default. The hero was waiting in the queue behind everything else on the page, which meant the browser did not even start fetching the LCP element until late. The fix is to tell the browser this image is the important one.
In Next.js 16, the priority prop is deprecated. It was replaced by clearer controls, because "priority" was doing three different things at once. Current guidance:
loading="eager" start loading immediately, do not lazy load fetchPriority="high" hint that this resource outranks others in the queue preload inject a preload link so the fetch starts from the document head
Use loading eager and fetchPriority high for a hero that is already in the initial HTML. Reach for preload only when the browser genuinely cannot discover the resource early, for example when the image URL is computed by client JavaScript. If you are on an older Next.js, the priority prop still works and does roughly the same thing.
Preloading is not free. A preload is an instruction to jump the queue, and the queue is finite. Preload the wrong resource and you have taken bandwidth away from the right one. We have watched a site preload three fonts, a video poster, and a background texture, and then wonder why the hero image got slower. One preload for the LCP element. That is usually the whole budget.
Also set sizes. Without it, the browser has to assume the image will display at full viewport width and it will pull a larger candidate from the srcset than a 375px phone needs. That is a resource load duration problem, not a discovery problem, and it is a different fix even though it lives on the same tag.
Element render delay: fonts were blocking text paint. The site loaded a Google Font through a plain link tag. The default font-display behavior blocks text rendering for up to about three seconds while the font loads. If your LCP element contains text, it cannot paint during that block.
The fix is font-display swap, which renders text immediately in a fallback and swaps when the custom font arrives. In Next.js the next/font module does this by default and self hosts the file. In plain CSS, add font-display swap to the font-face rule and preload the woff2. Accept the tradeoff honestly: swap trades a blank screen for a visible font change. That change is a layout shift risk, so pair it with a fallback whose metrics are close to the real font.
Element render delay again: the animation was the LCP. This was the one that surprised us. The hero used a JavaScript animation library to fade in on mount. The server sent the image in the HTML, React hydrated, the animation set the hero opacity to zero, and then faded it back to one. The browser does not count an element as painted until it is actually visible. LCP was measuring the end of a fade, not the arrival of an image.
Do not animate the LCP element on initial load. Animate below the fold, or trigger on scroll into view, or let the element paint and animate something else. This is worth checking on any site built with a motion library, because nothing in Lighthouse tells you that your own animation is the metric you are failing.
Time to first byte and render blocking scripts. Three third-party tags were loading synchronously in the head. Synchronous scripts in the head block parsing, which delays everything downstream including the discovery of your hero image. Defer them, load them after interactive, or move them out of the head. In Next.js the Script component takes a strategy of afterInteractive or lazyOnload. In plain HTML, add defer.
The result, stated for what it is
After the change set, the same Lighthouse mobile run reported LCP 1.8 seconds and performance 97, up from 7.3 seconds and 61. That is a lab result on one page. We did not run a controlled experiment, we did not measure the revenue effect, and we have no field data from before the change to compare against. What we can say is that the page went from a seven second blank screen to a fast one on a throttled mobile profile, and that the visual design did not change at all.
What we did not measure
We did not measure conversions, bounce rate, or rankings before and after. We did not collect field Core Web Vitals from real users. We did not isolate the contribution of each individual fix. If you see a post claiming that a specific font change delivered exactly 1.4 seconds, ask how many runs they averaged.
Do this in order
- Find the LCP element. Do not skip this. It is frequently not the image you assumed.
- Get the four phase numbers from DevTools or from field attribution data.
- Fix the largest phase first, and only the largest phase.
- Re-measure with the same throttling profile, several runs, and compare medians.
Our DeepAudit AI tool loads your page in Chromium on a mobile profile and reports LCP along with the rest of the rendered DOM inspection. It will tell you the number. It will not tell you which of the four phases owns it, so run the DevTools trace yourself before you start changing code.
If mobile performance is a real problem for your business, book a call and we will walk the trace with you.
Related services

Written by
Joshua R. GutierrezFounder & Lead Engineer, Axion Deep Digital
SEO strategist and full-stack engineer who builds the audit tooling, then does the work. Technical SEO, Core Web Vitals, and content systems for SaaS and B2B.
View Joshua R. Gutierrez’s full profile & credentials →Ready to build a website that performs?
Let us audit your current site, identify the biggest opportunities, and build a plan to grow your traffic and leads.