| name | chrome-devtools-lcp-debug |
| description | LCP (Largest Contentful Paint) debugging and optimization via Chrome DevTools MCP. Four subparts (TTFB/load-delay/load-duration/render-delay), performance trace workflow, insight analysis, optimization checklist. Good LCP ≤2.5s. Sources: ChromeDevTools/chrome-devtools-mcp (Apache-2.0). |
/chrome-devtools-lcp-debug
When to Use
- Page feels slow to load / Core Web Vitals failing (LCP > 2.5s)
- Hero image or main content takes too long to appear
- Need to identify which LCP subpart is the bottleneck before optimizing
- CrUX field data shows real-user LCP regression
Do NOT use for
- JavaScript runtime performance (use CPU profiler)
- Memory leaks (use [[chrome-devtools-memory-leak]])
- Accessibility issues (use [[chrome-devtools-a11y-audit]])
LCP thresholds
≤ 2.5s Good
2.5–4s Needs improvement
> 4s Poor
73% of mobile pages: LCP element is an image.
Four LCP subparts (no gaps, no overlaps)
┌───────────────────────────────────────────────────────────────────┐
│ TTFB │ Load delay │ Load duration │ Render delay │
│ (~40% of LCP) │ (<10%) │ (~40% of LCP) │ (<10%) │
└───────────────────────────────────────────────────────────────────┘
TTFB — navigation start → first byte of HTML received
Load delay — TTFB → browser starts loading LCP resource ← minimize
Load duration — time to download the LCP resource
Render delay — LCP resource downloaded → LCP element rendered ← minimize
"Delay" subparts should be near zero. A large delay = first optimization target.
⚠️ Common mistake: compress the LCP image (reduces load-duration) when render-delay
is the actual bottleneck → saved time just shifts to render-delay, no real gain.
ALWAYS check all four subparts first.
Step 1: Record performance trace
await mcp.call("navigate_page", { url: "https://example.com" });
const traceResult = await mcp.call("performance_start_trace", {
reload: true,
autoStop: true,
});
const insightSetId = traceResult.insightSets[0].id;
Step 2: Analyze LCP insights
const insightNames = ["LCPBreakdown", "DocumentLatency", "RenderBlocking", "LCPDiscovery"];
for (const insightName of insightNames) {
const insight = await mcp.call("performance_analyze_insight", {
insightSetId,
insightName,
});
console.log(`${insightName}:`, insight);
}
Step 3: Identify LCP element
const lcpData = await mcp.call("evaluate_script", {
script: `
new Promise(resolve => {
const observer = new PerformanceObserver(list => {
const entries = list.getEntries();
const last = entries[entries.length - 1];
resolve({
element: last.element?.tagName,
url: last.url,
size: last.size,
startTime: last.startTime,
renderTime: last.renderTime || last.loadTime,
});
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
})
`
});
Optimization by bottleneck
Subpart Bottleneck signal Fix
─────────────── ──────────────────────────── ────────────────────────────────────
TTFB (>1s) DocumentLatency insight high CDN, server-side caching, edge compute
Load delay LCPDiscovery late Add <link rel="preload"> for LCP image
Load duration LCP image download slow Compress image (WebP/AVIF), use CDN, resize
Render delay RenderBlocking insight Remove/defer render-blocking CSS/JS
Preload pattern (fixes load delay):
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">
fetchpriority="high" on the LCP <img>:
<img src="/hero.webp" fetchpriority="high" alt="Hero">
Quick size audit
const imageAudit = await mcp.call("evaluate_script", {
script: `
const img = document.querySelector('[fetchpriority="high"], img:first-of-type');
if (!img) return null;
return {
naturalW: img.naturalWidth,
naturalH: img.naturalHeight,
displayW: img.offsetWidth,
displayH: img.offsetHeight,
src: img.src,
waste: ((img.naturalWidth * img.naturalHeight) / (img.offsetWidth * img.offsetHeight)).toFixed(1) + 'x oversize'
};
`
});
Full trace to file (production)
await mcp.call("performance_start_trace", {
filePath: "/tmp/lcp-trace.json",
reload: true,
autoStop: true,
});
Anti-Fake-Pass Checklist
❌ Optimizing load-duration when render-delay is the bottleneck → saved time shifts, no real gain
❌ No preload for LCP image → browser discovers it late → high load delay
❌ LCP image without fetchpriority="high" → browser treats it as low-priority → high load delay
❌ Compressing image without checking display size first → may still be 10x oversized
❌ Only checking lab data (traces) — always cross-check with CrUX field data for real-user impact
❌ Recording trace without reload: true → misses TTFB and resource load events