First Contentful Paint (FCP) is a Web Vitals metric that measures the time from when a page starts loading to when the browser renders the first piece of DOM content — text, image, non-blank canvas, or SVG. Google considers FCP good when it happens in under 1.8 seconds at the 75th percentile of real users.
FCP is the first honest signal your page gives a visitor: "something is happening." Before FCP, the browser is a white screen. After FCP, the user knows the page is alive. That gap is where bounces are born.
What is First Contentful Paint (FCP)?
FCP is a page-load metric that fires the moment the browser paints the first piece of DOM content from the server response. That first content can be any of:
- Text (once fonts have loaded or fallback fonts are used)
- An
<img>(once decoded) - A non-blank
<canvas> - An SVG element
Background CSS images and iframes do not count. Neither does anything hidden with opacity: 0 or visibility: hidden. FCP is defined by the W3C Paint Timing API and reported by every modern browser through performance.getEntriesByType('paint').
FCP is one of the six metrics tracked in Chrome UX Report (CrUX) and PageSpeed Insights. It's not a Core Web Vital in itself, but Google uses it as a diagnostic and treats consistently slow FCP as a signal of poor page experience.
Why FCP matters
FCP is the earliest signal of perceived speed. Get it wrong and no other metric can save you.
- Bounce prevention. Users decide whether to wait or bail within the first 3 seconds. A white screen past 2 seconds materially lifts bounce rate — Google's own studies put the impact at 32% higher bounce probability going from 1s to 3s.
- LCP prerequisite. Largest Contentful Paint (the ranking-factor Core Web Vital) can't happen before FCP. Optimize FCP and LCP usually follows.
- Conversion signal. Amazon and Walmart have both published data showing every 100ms of load delay costs 1% of revenue. FCP is where those 100ms are usually hiding.
How FCP is measured
The Paint Timing API exposes FCP directly. Every Chromium browser (Chrome, Edge, Opera, Brave) records it and reports it to Google's CrUX dataset. Safari and Firefox measure it in DevTools but don't ship it to CrUX.
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
console.log('FCP:', entry.startTime, 'ms');
}
}
}).observe({ type: 'paint', buffered: true });
Where FCP is measured in the wild
Two data sources matter:
- Field data (CrUX) — real Chrome users. This is what Google uses for ranking signals.
- Lab data (Lighthouse) — a simulated single load in a controlled environment. Useful for debugging, not for grading.
FCP thresholds and where FCP fits in Web Vitals
| Metric | What it measures | Good | Poor |
|---|---|---|---|
| FCP | First content painted | < 1.8s | > 3.0s |
| LCP | Largest element painted | < 2.5s | > 4.0s |
| INP | Interaction to Next Paint | < 200ms | > 500ms |
| CLS | Cumulative Layout Shift | < 0.1 | > 0.25 |
| TTFB | Time to First Byte | < 800ms | > 1.8s |
Real FCP examples and fixes
Three common patterns account for most slow FCPs.
1. Render-blocking stylesheet
<link rel="stylesheet" href="/theme.css">
# After: FCP 1.4s — critical CSS inlined, rest deferred
<style> /* critical above-the-fold rules */ </style>
<link rel="preload" href="/theme.css" as="style" onload="this.rel='stylesheet'">
2. Slow origin, no CDN
A blog on a single VPS in Frankfurt served visitors globally with TTFB of 1.4s. Moving to a CDN with 30+ POPs dropped TTFB to 180ms and FCP from 3.1s to 1.2s — no code changes.
3. Third-party font blocking text render
An e-commerce site loaded Google Fonts synchronously. Text stayed invisible until fonts arrived — FCP effectively pushed to 2.9s. Adding font-display: swap pulled FCP back under 1.5s by letting the browser paint fallback fonts immediately.
FCP vs. LCP — which one matters more
Both measure paint. The difference is what gets painted.
Optimize FCP when
- Your page shows a long white screen
- TTFB is over 800ms
- Render-blocking CSS/JS is present
- You want to reduce perceived load time
- You're chasing Lighthouse score improvements
Prioritize LCP when
- FCP is already under 1.8s
- Your hero image is late or oversized
- You're chasing Core Web Vitals ranking
- Above-the-fold content is heavy
- Google Search Console flags LCP issues
7 best practices to hit FCP under 1.8s
- Cut TTFB first. Every millisecond of server delay pushes FCP later. Cache HTML at the edge, upgrade hosting, or move to a CDN.
- Inline critical CSS. Extract the CSS needed for the first viewport (usually under 14KB) and inline it in
<head>. - Defer non-critical JavaScript. Add
deferorasyncto script tags so JS parsing doesn't block the paint. - Use
font-display: swap. Never let web fonts hide text. Show fallback fonts immediately, swap when ready. - Preconnect to critical origins. Add
<link rel="preconnect">for CDNs and font hosts to open the TCP+TLS handshake early. - Serve WebP / AVIF images. Modern formats cut image bytes by 25-50%, so above-the-fold images paint faster.
- Eliminate 3XX redirect chains. Every hop costs a full round trip. Redirect once, directly to the final URL.
Lighthouse runs a single simulated load on your dev machine. CrUX shows real users on real devices at the 75th percentile. Chasing a green Lighthouse score won't help ranking if your CrUX FCP is still over 3s. Always verify improvements in Search Console's Core Web Vitals report.
Common FCP mistakes to avoid
- Loading web fonts synchronously — hides all text until fonts arrive.
- Blocking JavaScript in
<head>— parser stops until the script downloads and executes. - Massive bundled CSS — 300KB stylesheets block the paint even when 90% of the rules aren't needed above the fold.
- Client-side rendering on cold visits — SPAs that render the entire DOM in JS often have FCP over 3s.
- Skipping
preconnect— cross-origin requests eat 200-400ms per handshake without it. - Testing on desktop only — mobile is where FCP is graded. Test on throttled 4G, not fiber.
Frequently asked questions
FCP is the moment the browser paints the first bit of real content — text, an image, or an SVG — after someone clicks your link. It's the first sign that the page is alive.
Google considers FCP good under 1.8 seconds, needs improvement between 1.8 and 3.0 seconds, and poor above 3.0 seconds. These thresholds are measured at the 75th percentile of real users.
FCP is not one of Google's three Core Web Vitals, but it's part of the Page Experience signals and is closely correlated with LCP, which is a ranking factor. Improving FCP almost always improves LCP too.
FCP measures the first pixel of content painted. LCP (Largest Contentful Paint) measures when the largest visible element finishes rendering. FCP tells you the page started loading; LCP tells you it's usable.
Reduce server response time, eliminate render-blocking resources (JS and CSS), inline critical CSS, preconnect to required origins, and serve static assets from a CDN. Each optimization typically cuts hundreds of milliseconds.
