FCP, short for First Contentful Paint, is the web performance metric that measures the time from the start of navigation until the browser renders the first piece of DOM content, such as text, an image, a non-white canvas or an SVG element. It answers a simple user question: when does the page visibly start to load?
In web performance work, FCP sits between server-side timing and full-content metrics. It comes after TTFB, which reflects how quickly the first byte arrives, and usually before LCP, which marks the moment the largest visible element appears. A fast FCP reassures visitors that something is happening, while a slow FCP is often the first sign of render-blocking resources or a sluggish server.
How the browser determines FCP
The browser records FCP at the first frame that paints contentful DOM content. The timestamp is measured relative to the navigation start, so it includes redirects, DNS lookup, connection setup, server response, HTML parsing, style calculation, layout and the paint itself.
The rule for what counts is precise. Text nodes, images, background images, SVG and non-white canvas elements qualify. A white background, a plain colored body or an empty container does not qualify, which is why First Paint (FP) and FCP can differ: FP fires on any pixel change, while FCP waits for real content. Content inside iframes is not included in the parent page’s FCP.
The table below shows which rendered items trigger the metric and which do not.
| Rendered item | Counts for FCP | Note |
|---|---|---|
| Visible text | Yes | Includes text drawn with a fallback font |
| Image or background image | Yes | Any decoded image that paints pixels |
| SVG element | Yes | Inline or referenced vector graphics |
| Canvas | Only if non-white | A blank canvas does not qualify |
| Page background color | No | Triggers First Paint only |
| Iframe content | No | Measured within the iframe’s own document |
In code, the value is exposed through the Paint Timing API. A PerformanceObserver listening for entries of type paint receives an entry named first-contentful-paint, whose startTime is the FCP in milliseconds. Real-user monitoring scripts read this entry and report it together with other metrics.
FCP thresholds and how they are scored
FCP is judged against widely used thresholds measured at the 75th percentile of page loads, segmented by mobile and desktop. Using the 75th percentile means most visitors, not only those on fast connections, must have a good experience for a page to pass.
| Rating | FCP value | Interpretation |
|---|---|---|
| Good | 1.8 s or less | Visitors see content quickly |
| Needs improvement | Over 1.8 s up to 3.0 s | Noticeable blank period |
| Poor | Over 3.0 s | High risk of abandonment and frustration |
Lab tools such as Lighthouse also include FCP in their composite performance score, where it carries a modest weight compared with LCP and total blocking time. The exact weighting changes between tool versions, so teams should treat the lab score as a diagnostic aid rather than a target in itself.
FCP is not one of the Core Web Vitals. It remains a supporting diagnostic metric: a slow FCP almost always produces a slow LCP, because the largest element cannot appear before the first content does.
FCP compared with neighboring loading metrics
FCP is best understood as one step in a chain of loading milestones. Each milestone isolates a different part of the loading process, and comparing them shows where time is lost.
| Metric | What it marks | Main influences |
|---|---|---|
| TTFB | First byte of the HTML response | Server work, caching, network latency |
| First Paint | Any pixel change after navigation | Background styles, early rendering |
| FCP | First text, image, SVG or canvas content | Render-blocking CSS and scripts, fonts |
| LCP | Largest visible image or text block | Hero image size, priority, rendering |
| Speed index | How quickly the viewport fills visually | Progressive rendering of all content |
The gap between TTFB and FCP is especially informative. A small gap means the browser can paint almost as soon as the HTML arrives. A large gap, often over one second on mobile, points to blocking stylesheets, synchronous scripts in the document head or web fonts that hide text while they download.
Techniques that improve FCP
Improving FCP means shortening the critical rendering path, the sequence of resources the browser must fetch and process before it can paint. Every resource that blocks rendering adds network round trips and processing time before the first frame.
| Technique | What it changes | Typical effect |
|---|---|---|
| Reduce server response time | Caching, edge delivery, faster backend | Lowers TTFB and FCP together |
| Inline critical CSS | Above-the-fold styles in the HTML | Removes a blocking stylesheet request |
| Defer non-critical scripts | defer or async on script tags | Parser no longer stops for JavaScript |
| Use font-display swap | Text shown in a fallback font first | Text paints before the web font loads |
| Preload key resources | rel=preload for critical fonts or CSS | Resources start downloading earlier |
| Reduce redirects | Direct links to the final URL | Removes whole round trips |
Rendering strategy matters as well. Pages built with server-side rendering or static generation deliver ready HTML and usually paint sooner than pages that rely on client-side rendering, where an empty shell waits for a JavaScript bundle to download, parse and execute before any content appears.
Pitfalls when interpreting FCP
FCP is easy to measure but easy to misread. The most important limitation is that it says nothing about whether the first content is useful; a cookie banner, a loading spinner or a header logo can trigger FCP while the main content is still missing.
- Spinners and skeletons can flatter FCP without improving the experience, because they count as content while real information is absent.
- Lab and field values differ. A test on a fast machine may show 0.8 s while real visitors on mid-range phones experience 2.5 s or more.
- Back-forward cache restores and prerendered pages can report very low or adjusted values, so they should be segmented in reports.
- Background tabs distort timing, because browsers delay rendering for pages that are not visible; monitoring scripts should ignore such loads.
- Averages hide the slow tail. The 75th percentile, not the mean, reflects what a substantial share of visitors experience.
For these reasons, FCP should be read together with LCP, CLS and INP rather than in isolation. A good FCP combined with a poor LCP usually means the page shows a frame quickly but delays the content that visitors came for.
Understanding it simply
FCP measures how long a visitor stares at a blank screen. The clock starts when someone clicks a link and stops the moment anything meaningful, such as a headline or a picture, appears on the page.
Think of entering a restaurant. Being greeted at the door does not mean the meal has arrived, but it tells you that someone has noticed you and the service has started. FCP is that greeting: a quick one keeps people patient, while a long silence makes them wonder whether to leave.
Applied in practice
An online retailer notices in its field data that mobile visitors experience an FCP of 3.4 seconds at the 75th percentile, which falls in the poor band. Investigation shows a TTFB of 0.6 s, so the server is not the main problem. The waterfall reveals two large stylesheets and a synchronous analytics script in the document head, plus a web font configured to hide text until it loads.
The team inlines about 12 KB of critical CSS, loads the remaining styles asynchronously, adds defer to the analytics script and switches the font to font-display: swap. After deployment, the 75th percentile FCP drops to 1.6 seconds, moving the page into the good band, and LCP improves as well because the hero image no longer waits behind blocking resources.