First Meaningful Paint (FMP) was deprecated in Lighthouse 6.0 because it relied on heuristics that produced inconsistent results across browsers and tools. Largest Contentful Paint (LCP) replaced it with an objective, reproducible measurement. This guide walks through the migration: what changes, what stays the same, and how to update your monitoring.
Why the switch happened
FMP tried to detect when the "most important" content appeared on screen by tracking layout changes and picking the biggest visual shift after First Contentful Paint. The problem: "important" is subjective.
Two different tools could report two different FMP values for the same page load because they disagreed on which layout change mattered most. LCP eliminates that ambiguity by measuring the render time of the largest image or text block visible in the viewport.
Key differences:
| Aspect | FMP | LCP |
|---|---|---|
| What it measures | Biggest layout change (heuristic) | Largest visible element (objective) |
| Consistency | Varied across tools | Consistent across tools |
| Browser support | Removed from Chrome | All modern browsers |
| Lighthouse version | Removed in v6.0 | Current standard |
| Correlation with UX | Moderate | Strong |
Step 1: Map your current FMP targets to LCP
If you had FMP performance budgets, translate them:
- FMP < 2s → target LCP < 2.5s (good)
- FMP 2–3s → target LCP < 4.0s (needs improvement)
- FMP > 3s → target LCP < 4.0s minimum, ideally < 2.5s
Do not assume a fixed ratio. Measure actual LCP on your pages first, then set realistic targets based on your architecture.
Step 2: Identify your LCP element
Every page has a single LCP element — the largest image, video poster, or text block in the initial viewport. Find it:
- Open Chrome DevTools → Performance panel
- Record a page load
- Look for the "LCP" marker in the timeline
- Click it to see which DOM element triggered it
Common LCP elements by page type:
- Landing pages: hero image or background image
- Blog posts: featured image or first heading + paragraph block
- Product pages: primary product image
- Search results: first result card or list container
Step 3: Update your measurement code
Replace any FMP-specific measurement with the PerformanceObserver API for LCP:
// LCP measurement
new PerformanceObserver(list => {
const entries = list.getEntries()
const lastEntry = entries[entries.length - 1]
console.log("LCP:", lastEntry.startTime, "ms")
console.log("Element:", lastEntry.element)
}).observe({ type: "largest-contentful-paint", buffered: true })
For custom "meaningful" tracking, use the Element Timing API:
<img elementtiming="hero-image" src="hero.jpg" alt="Hero" />
<script>
new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
console.log(`${entry.identifier}: ${entry.startTime}ms`)
})
}).observe({ type: "element", buffered: true })
</script>
Step 4: Update CI/CD performance budgets
Replace FMP assertions with LCP in your Lighthouse CI config:
// lighthouse-ci.config.js
module.exports = {
ci: {
assert: {
assertions: {
"largest-contentful-paint": ["error", { maxNumericValue: 2500 }],
"first-contentful-paint": ["warn", { maxNumericValue: 1800 }],
"total-blocking-time": ["error", { maxNumericValue: 200 }],
"cumulative-layout-shift": ["error", { maxNumericValue: 0.1 }]
}
}
}
}
Remove any first-meaningful-paint assertions — they will cause errors in Lighthouse 10+.
Step 5: Optimize specifically for LCP
LCP optimization differs from FMP optimization because you know the exact target element.
For image LCP elements
- Add
fetchpriority="high"andloading="eager"to the hero image - Preload the image with
<link rel="preload" as="image" href="hero.jpg"> - Serve correctly sized images (avoid layout shifts from dimension changes)
- Use modern formats (WebP, AVIF) with appropriate fallbacks
For text LCP elements
- Inline critical CSS or use
<link rel="preload">for the font - Avoid
font-display: swapflash — usefont-display: optionalif speed is priority - Minimize render-blocking JavaScript before the text block
- Reduce server response time (TTFB directly impacts text LCP)
For both
- Reduce TTFB below 800ms (server, CDN, caching)
- Eliminate render-blocking resources above the fold
- Avoid lazy-loading the LCP element (a common mistake with blanket lazy-load policies)
Step 6: Monitor in the field
Lab metrics (Lighthouse) show potential. Field metrics (CrUX, web-vitals library) show reality.
Set up field monitoring with the web-vitals library:
import { onLCP } from "web-vitals"
onLCP(metric => {
// Send to your analytics endpoint
analytics.send({
name: metric.name,
value: metric.value,
rating: metric.rating, // 'good', 'needs-improvement', 'poor'
navigationType: metric.navigationType
})
})
Compare your field LCP against your old FMP baselines. If LCP is significantly higher than FMP was, investigate whether your LCP element is loading slowly (image optimization issue) or rendering late (render-blocking resources issue).
Common migration pitfalls
Pitfall 1: Assuming FMP ≈ LCP
They measure different things. A page with a fast layout shift (good FMP) but a slow hero image (bad LCP) will look worse after migration. That is not a regression — it is a more accurate measurement.
Pitfall 2: Lazy-loading the LCP element
Many teams apply loading="lazy" to all images. The LCP image must use loading="eager" or have no loading attribute at all.
Pitfall 3: Ignoring TTFB
LCP cannot be faster than your server response time. If TTFB is 2 seconds, LCP will be at least 2 seconds. Optimize the server path first.
Pitfall 4: Over-optimizing lab scores
A lab LCP of 1.2s on a fast machine means little if field data shows 4.5s on real devices. Always validate with CrUX or RUM data.
Frequently Asked Questions
Can I still track "meaningful" content timing?
Yes. Use the Element Timing API to measure specific elements you consider meaningful. This gives you the custom insight FMP promised without the inconsistency. Mark elements with elementtiming="label" and observe them with PerformanceObserver.
Does LCP affect SEO rankings?
LCP is part of Core Web Vitals, which is a confirmed Google ranking signal. Pages with good LCP (under 2.5s) have a ranking advantage over pages with poor LCP, all else being equal.
What if my LCP element changes between page loads?
This can happen with A/B tests, personalized content, or ad placements. Identify all possible LCP candidates and ensure each one loads quickly. Use the Element Timing API to track which element wins on each load.
How often should I check LCP?
Monitor field data continuously via CrUX or a RUM provider. Run Lighthouse audits in CI on every deploy. Review the 75th percentile LCP in CrUX monthly to track trends.