What is FCP (First Contentful Paint)?
Definition
FCP (First Contentful Paint) measures the time from when a user starts navigating to a page until any part of its content (text, an image, an SVG or a non-white canvas) is first rendered on screen. It marks the first visual sign that the page is actually loading. web.dev rates 1.8 seconds or less at the 75th percentile as good and anything above 3 seconds as poor.
Also known as: First Contentful Paint, first paint of content

What counts as contentful
FCP records the moment the screen stops being blank and shows the visitor something real. Not every pixel qualifies: text, images (including CSS background images), <svg> elements and non-white <canvas> elements do; a painted background colour or an empty canvas does not.
Web fonts are a common hidden factor. If the browser keeps text invisible while a font downloads, FCP waits even though the text is already in the DOM. Paints inside cross-origin iframes may also go unreported by the Paint Timing API, one of several reasons field and lab numbers drift apart.
First paint versus largest paint
FCP looks at the first piece of content; LCP looks at the largest one in the viewport. On a typical article page, FCP fires when the header and headline appear, LCP when the hero image finishes. The gap is diagnostic. A fast FCP with a slow LCP usually points to the main image being discovered late or being too heavy. When both are slow, the bottleneck sits earlier in the chain: the server response or resources that block rendering.
Thresholds in the field and in Lighthouse
| Source | Good | Moderate | Poor |
|---|---|---|---|
| Field (web.dev, 75th percentile) | ≤ 1.8 s | 1.8–3 s | > 3 s |
| Lighthouse, mobile | ≤ 1.8 s | 1.8–3 s | > 3 s |
| Lighthouse, desktop | ≤ 0.9 s | 0.9–1.6 s | > 1.6 s |
FCP carries 10% of the Lighthouse performance score. Field FCP also absorbs unload time of the previous page, redirects, connection setup and TTFB, so a clean lab run tends to look better than what real users get. FCP is not a Core Web Vital, but it is a precondition for a good LCP: the largest paint can never happen before the first one.
What pushes FCP back
- Blocking CSS and JavaScript. Synchronous scripts and large stylesheets in the
<head>must download and run before anything paints; see render-blocking resources. - Invisible text. Hiding text until a font arrives.
font-displaylets the browser show a fallback immediately:@font-face { font-family: "Brand"; src: url("/fonts/brand.woff2") format("woff2"); font-display: swap; } - A slow first response. Front-end tuning can't compensate for a server that answers late.
- An empty app shell. When all markup is generated client-side, the first paint waits for the JavaScript bundle to download and execute.
A sensible order of work: server response first, then web fonts and critical styles, and third-party scripts last.

