Why Your Pages Pass Core Web Vitals on Desktop but Fail on Mobile

This guide covers one of the most common reports in Search Console, within Understanding Core Web Vitals Thresholds and the broader Core Web Vitals & Measurement section: desktop URLs are green, mobile URLs are amber or red, and the code is identical.

Core Web Vitals are assessed separately per form factor because the conditions are not comparable. A mid-range Android phone executes JavaScript three to five times slower than a typical laptop, sits on a network with higher and more variable latency, renders a different layout with a different LCP element, and often has less memory for caches. The same page can be at 1.6s LCP and 90ms INP on desktop and 3.2s and 340ms on mobile — without anything being "broken".

The same page on two form factors Comparison of field conditions and metric outcomes for one page on desktop and mobile, showing CPU, network, LCP element and resulting p75 values. The same page on two form factors Factor Desktop p75 visit Mobile p75 visit CPU speed (relative) 1x 3-5x slower Round-trip time 30-60ms 100-300ms LCP element hero image, 1200px H1 or small hero LCP p75 1.6s 3.2s INP p75 90ms 340ms

Rapid Diagnosis

  • Confirm the split in CrUX. Query the CrUX API with formFactor: PHONE and DESKTOP separately. Note which metrics fail on phone only.
  • Check the share of mobile traffic. If mobile is 70%+ of visits, the origin-level assessment is effectively the mobile one.
  • Profile on a real mid-tier device (or CPU throttling at 4x for LCP and 6x for INP work). Laptop profiling without throttling hides everything this guide is about.
  • Look at the mobile LCP element. Run the page at 390px wide and log LCP candidates; it is often a different element from desktop.
  • Count main-thread work. In a throttled trace, total scripting time during load is the single best predictor of mobile INP problems.

Root Cause Analysis

1. CPU-bound work scales badly. JavaScript parse, compile and execution, style recalculation and layout all scale with CPU speed. A 300ms hydration task on a laptop becomes a 1.2s task on a mid-tier phone. Any interaction that overlaps it fails INP; any LCP that waits for it is delayed.

2. Latency multiplies round trips. Every sequential round trip — DNS, TCP, TLS, redirect, HTML, CSS, font, image — costs RTT on mobile networks where RTT is several times higher. Pages with deep request chains degrade disproportionately.

3. Different layout, different LCP. Responsive designs reflow. A desktop hero might be hidden on mobile, making a heading or a product image the LCP element — with different dependencies and different timing.

4. Mobile-only third parties and UI. App-install banners, mobile ad formats, sticky footers and different consent UIs often run only on small viewports, adding main-thread work and layout shift that desktop never sees.

Hydration task duration by device class Bar chart of the same hydration long task measured on four device classes, crossing the 200 millisecond INP budget on mid-tier and low-end phones. Hydration task duration by device class Desktop laptop 140ms Flagship phone 190ms Mid-tier Android 520ms Low-end Android 980ms 200ms INP budget

Step-by-Step Resolution

1. Reproduce mobile conditions honestly

javascript
// puppeteer: emulate a mid-tier phone for a lab baseline that resembles mobile p75.
await page.emulate(KnownDevices['Moto G Power']);
const client = await page.createCDPSession();
await client.send('Emulation.setCPUThrottlingRate', { rate: 4 });
await client.send('Network.emulateNetworkConditions', {
  offline: false, latency: 150, downloadThroughput: 1.6 * 1024 * 1024 / 8,
  uploadThroughput: 750 * 1024 / 8,
});
// trade-off: emulation approximates CPU and network but not thermal throttling,
// memory pressure or GPU limits. Validate key findings on a physical device.

Expected outcome: a lab trace whose LCP and long tasks resemble the mobile field p75 within roughly 20–30%.

2. Cut JavaScript execution on the critical path

Mobile INP and LCP failures trace back to main-thread work far more often than to bytes. Defer what is not needed for the first view, split hydration, and yield in long tasks.

javascript
// Load non-critical widgets only on mobile after first input or idle.
const deferUntilReady = (load) => {
  const run = () => { cleanup(); load(); };
  const cleanup = () => ['pointerdown', 'keydown', 'scroll'].forEach((t) => removeEventListener(t, run));
  ['pointerdown', 'keydown', 'scroll'].forEach((t) => addEventListener(t, run, { once: true, passive: true }));
  requestIdleCallback(run, { timeout: 4000 });
};
deferUntilReady(() => import('./reviews-widget.js'));
// trade-off: deferring until first input means the widget may not be ready when
// a user scrolls to it quickly. Render a lightweight placeholder with real
// dimensions so the late arrival does not shift layout.

Expected outcome: Total Blocking Time in the throttled trace drops substantially; INP on mobile typically improves by 100–250ms. The fuller playbook is in reducing Total Blocking Time in Lighthouse.

3. Remove round trips from the LCP path

Inline critical CSS, preload the mobile LCP resource, preconnect to required origins, and eliminate redirects. Each removed round trip is worth 100–300ms on mobile and only 30–60ms on desktop — exactly the asymmetry you need.

4. Optimise the mobile LCP element specifically

Serve a mobile-sized hero through srcset/sizes or art direction, and if the heading is LCP on mobile, optimise for text: critical CSS and font strategy.

html
<picture>
  <source media="(max-width: 600px)" srcset="/img/hero-mobile-600.avif" type="image/avif">
  <source srcset="/img/hero-1400.avif" type="image/avif">
  <img src="/img/hero-1400.jpg" width="1400" height="700" fetchpriority="high" alt="…">
</picture>
<!-- trade-off: art-directed mobile crops add an asset to maintain per hero. If
     the crop is the same image scaled, srcset alone is simpler and sufficient. -->

Expected outcome: mobile LCP improves without changing desktop; see art direction with the picture element.

Order of work for a mobile-only failure Four ordered steps to fix Core Web Vitals that fail only on mobile, from honest reproduction to element-specific optimisation. Order of work for a mobile-only failure Reproduce at 4x CPU and 150ms RTT Match the lab to the mobile p75 before changing anything Cut main-thread work Defer widgets, split hydration, yield long tasks Remove round trips Critical CSS, preload, preconnect, no redirects Optimise the mobile LCP element Mobile-sized images or text-first strategy 1 2 3 4

Verification

Re-run the throttled lab baseline and compare LCP, TBT and the slowest scripted interaction. In the field, track mobile and desktop separately in your RUM and expect mobile to improve much more than desktop for the same change — if desktop improves more, the fix was not targeted at the mobile bottleneck. CrUX's phone form factor will follow over the 28-day window.

Device Mix Matters More Than You Think

Two sites with identical templates can have very different mobile p75s because of who visits them. An audience in markets where low-end Android devices dominate will show a mobile p75 several hundred milliseconds worse than an audience on recent iPhones. Segment your RUM by device memory and CPU core count to see your actual mix, and set your lab throttling from it rather than from a generic profile. Where the low-end share is large, consider serving a lighter experience — fewer widgets, simpler animations — based on navigator.deviceMemory or Client Hints, rather than trying to make the full experience fast on every device.

Mobile-Only Code Paths to Audit

Responsive sites accumulate behaviour that only runs on small screens, and it is rarely profiled because developers work on wide monitors. Search the codebase and tag manager for these before assuming the shared code is the problem.

Viewport-conditional scripts. matchMedia('(max-width: 768px)') branches that load a mobile navigation library, a swipe gesture handler or a bottom-sheet component. Each is an extra chunk and extra startup work on exactly the devices least able to afford it. Load them on first interaction with the control that needs them.

Mobile ad formats and sticky units. Anchor ads and interstitials are often configured only for mobile inventory. They insert content after load, frequently above the fold, and run auction scripts on the main thread. They are a common source of mobile-only CLS and INP failures; reserve their space and load them after the main content.

Touch event listeners that are not passive. A non-passive touchstart or touchmove listener on the document forces the browser to wait for JavaScript before scrolling. It does not affect INP directly, but it makes the page feel sluggish and often coincides with heavy handlers that do.

Larger DOM from hidden desktop content. Many responsive layouts render both the desktop and mobile versions of navigation and hide one with CSS. The hidden markup still costs parse, style and memory. On complex mega-menus this can be thousands of nodes; render the mobile variant only on mobile, server-side, using Client Hints or a separate layout.

Images selected for the wrong viewport. A sizes attribute missing or set to 100vw everywhere means mobile devices with high DPR download images two or three times larger than needed — a pure mobile LCP cost.

FAQ

Does Google rank mobile and desktop separately?

Search uses mobile-first indexing, and page experience signals are evaluated per form factor. In practice, the mobile assessment is the one that matters for most sites, because most search traffic is mobile. A desktop pass does not compensate for a mobile failure.

Should tablets count as mobile?

CrUX reports tablets as their own form factor, and Search Console groups them with mobile for its report. Tablets usually have more CPU than phones, so they rarely drive failures, but their layouts can promote different LCP elements. Check them in RUM, but prioritise phones.