Skip to content

SpeedPart of: Page speed

First Contentful Paint and Speed Index: what they measure and what moves them

First Contentful Paint (FCP) is the moment the first text or image appears; Speed Index is how quickly the rest of the screen fills in. What each measures, good values, how they relate to LCP, and the fixes that move them.

getReport teamUpdated 27 Sept 202614 min read

First Contentful Paint (FCP) is the moment the first text, image, SVG or drawn canvas appears on screen after a visitor opens a page; it is good at 1.8 s or less. Speed Index is how quickly the rest of the visible area fills in after that, good at 3.4 s or less on mobile. Neither is a Core Web Vital or a ranking signal, but together they are 20 % of the Lighthouse score and they explain why Largest Contentful Paint is late. This guide explains what each measures, why Speed Index exists only in the lab, and the fixes that move both, in order. For how they fit with the other speed metrics, see the page speed guide.

Quick answer

MetricWhat it measuresGoodPoorWeight in the score
First Contentful Paint (FCP)The first text, image, canvas or SVG painted≤ 1.8 s> 3.0 s10 %
Speed Index (SI)How quickly the visible area became visually complete≤ 3.4 s> 5.8 s10 %
  • FCP is decided by what happens before any pixel: server response, redirects, render-blocking CSS and JavaScript, and fonts that hide text.
  • Speed Index is decided by what happens after the first pixel: above-the-fold images, lazy-loaded heroes, skeleton screens, sliders and client-side rendering.
  • Fix FCP first (it is the floor of every other metric), then look at the filmstrip for Speed Index.
  • Run the speed test; both numbers are in the lab table, next to LCP.

What is First Contentful Paint?

FCP is a browser timestamp: the first time the page paints anything that counts as content, in Google's definition: text (including text in a web font that has arrived), an <img>, an SVG, or a <canvas> with something drawn on it. A background colour does not count; a border does not count. A page that paints a white header bar with a logo at 0.9 s has an FCP of 0.9 s even if the rest is blank.

FCP matters because a visitor who taps a link and sees nothing cannot tell whether the page is loading or the network is dead. It is also the floor of every later metric: a 2.5 s FCP guarantees an LCP over 2.5 s before a single image is involved.

FCP can be measured on real visitors: Chrome reports it and the Chrome UX Report publishes it, so you may see it in field data next to the Core Web Vitals. Google chose LCP as the loading vital instead because "first pixel" is too easy to satisfy with a spinner.

What is Speed Index?

Speed Index catches a different failure. A page can paint its header at 1.2 s and then show a grey skeleton for three more seconds while JavaScript fetches the products, or paint everything except the hero, which pops in at 4 s. FCP is fine in both cases and the visitor still waits.

Lighthouse captures screenshots of the load, compares every frame with the final one to work out how "visually complete" the viewport is at that moment (0 % to 100 %), and adds up the incompleteness over time. A page that goes from 0 % to 100 % at 1.5 s in one step has a Speed Index of 1.5 s. A page that is blank until 1.5 s, 80 % complete from then, and 100 % at 6 s has a Speed Index of about 2.4 s, because most of the area arrived early. The number is in milliseconds, but it is an average, not a moment. Because it needs screenshots of the whole load, Speed Index exists only in lab tests.

How getReport checks it

The speed module asks Google's PageSpeed Insights for a Lighthouse run of the page (mobile by default: a simulated slow 4G connection and a 4× slower CPU) and shows the six lab metrics as one table, each with its value and a good, needs-work or poor rating:

The speed panel's lab table: six rows for First Contentful Paint, Largest Contentful Paint, Total Blocking Time, Cumulative Layout Shift, Speed Index and Time to First Byte, each with a value and a colour-coded rating chip, and the LCP element line beneath
Read the table top to bottom as a timeline: TTFB sets the floor, FCP is the first pixel, Speed Index and LCP say how the rest of the screen arrived.

Below the table the filmstrip shows what the screen looked like at each moment, which is the Speed Index made visible. The four findings that matter for these metrics:

The render-blocking finding lists the files between the first byte and the first paint with the delay Lighthouse estimates for each (Lighthouse 13 calls this insight "Render-blocking requests").

Step by step

1. Subtract TTFB from FCP

FCP includes the server response. If TTFB is 1.4 s and FCP is 2.1 s, the page itself is doing fine (0.7 s from HTML to paint); the server is the problem, and no CSS change will fix it. See TTFB: what a slow server looks like. If TTFB is 0.2 s and FCP is 2.1 s, 1.9 s went into render-blocking files and fonts; continue here.

Redirects count too. Every hop before the page (http:// → https:// → www. → trailing slash) is at least one full round trip on the lab's 150 ms connection, and a hop to a new host needs a new connection and TLS handshake. The redirects finding lists them.

2. Clear the path to the first paint

Between the HTML arriving and the first pixel, the browser must download and parse every stylesheet in <head> and every script without defer or async. The fixes, from cheapest:

HTML
<!-- Scripts not needed for the first paint: defer keeps order, async does not -->
<script src="/js/app.js" defer></script>
<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXX" async></script>

<!-- Stylesheets only needed later: load without blocking -->
<link rel="stylesheet" href="/css/below-the-fold.css" media="print" onload="this.media='all'">

For the main stylesheet the honest options are to make it smaller (remove unused rules, split per template) or to inline the rules needed for the first screen and load the rest asynchronously. Both are covered in Render-blocking resources.

3. Let text paint before the font arrives

Web fonts are the most common reason FCP is later than the CSS timing suggests. With the default font-display: auto, Chrome and Firefox hide text in a web font for up to 3 s while it downloads, and hidden text is not a contentful paint. If the only content above the fold is text, FCP is the font's arrival time.

CSS
@font-face {
  font-family: "Instrument Sans";
  src: url("/fonts/instrument-sans-var.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap; /* paint in the fallback font now, swap when the web font arrives */
}

For Google Fonts, add &display=swap to the stylesheet URL. Then preload the one or two fonts used in the first screen so the swap happens early: see Web fonts without layout shift.

4. Compress and shorten the HTML

The first paint cannot happen until enough HTML has arrived to render something. A 300 KB uncompressed HTML document (page builders and inline SVG icons produce these) takes well over a second on the lab connection. Brotli or gzip typically cuts HTML by 70-80 %. Also look for large inline <style> and <script> blocks in <head>: inlining 200 KB of CSS "to avoid a request" moves the delay into the HTML.

5. Now read the filmstrip for Speed Index

With FCP under control, open the filmstrip and find the frame where the page first looks like the final one. The gap between the first non-blank frame and that frame is what Speed Index measures. Common shapes, and what each means:

  • Header at 1 s, everything else at 4 s. The content is rendered by JavaScript after an API call. Server-render the first screen: the HTML should contain the products, not an empty <div id="app">.
  • Everything except the hero image, then the image at 3.5 s. The hero is lazy-loaded, set in CSS, or loaded by a slider script. Use a real <img> in the HTML with fetchpriority="high" and no loading="lazy"; see Lazy loading done right.
  • Grey boxes (a skeleton) for two seconds, then content. Skeletons count as painted, so they lower FCP and raise Speed Index, because the final frame has no grey boxes. Only worth it when the real content genuinely cannot be in the HTML.
  • A slider that cycles during the load. Every slide change is a visual change, so the page never looks "complete". Do not autoplay during load, or make the first slide static.
  • A cookie banner slides in at 2.5 s. The banner changes the frame. Render it in the HTML from the start rather than injecting it after a script loads.

Reading a waterfall chart shows how to match each frame to the file that caused it.

6. Check that above-the-fold images are sized and early

Speed Index is a viewport metric, so only what is on screen at 412 × 823 px (the lab's phone viewport) counts. A category page with six product images in the first screen fills in as fast as the slowest of them. Give each an srcset so the phone downloads a 400 px file and not a 1600 px one, keep them as <img> in the HTML, and do not lazy-load the first row.

Platform notes

WordPress. The usual FCP causes are, in order: no page cache (TTFB), the theme and page builder enqueueing many stylesheets in <head>, a Google Fonts stylesheet without display=swap, and jQuery-dependent scripts in the head. Caching plugins such as WP Rocket and LiteSpeed Cache have settings to load CSS asynchronously, remove unused CSS and defer JavaScript. For Speed Index, the offenders are sliders as the hero and lazy-loading plugins that catch the first image.

Shopify. TTFB and compression are the platform's and already good. FCP is usually theme scripts in theme.liquid without defer and apps that inject scripts into <head>; the theme editor's "App embeds" list shows which ones you can turn off. For Speed Index, use a static hero image rather than a slideshow.

Static sites and JavaScript apps. If FCP is high on a static site, it is almost always fonts or a large blocking script bundle. On a JavaScript app, view the page source and search for a headline you see on screen: if it is not there, the framework renders on the client and every visitor waits for the bundle. Static generation or server rendering for the entry pages is the fix.

Verify

  • FCP under 1.8 s and Speed Index under 3.4 s on the mobile run, with both rows rated good.
  • The filmstrip shows the first non-blank frame and the "looks complete" frame within one or two frames of each other.
  • Run the test three times; FCP should agree within about 200 ms. A larger spread points at the server or a third party.
  • LCP moved with it. If FCP improved and LCP did not, the LCP element itself is the next job: see Largest Contentful Paint.

Common mistakes

  • Optimising FCP with a spinner. FCP looks great and the visitor still sees nothing useful; Speed Index and LCP get worse.
  • Inlining all the CSS. Every later page view downloads it again inside the HTML, uncached. Inline the first-screen rules only (10-20 KB).
  • Reading Speed Index on desktop. The desktop viewport is 1350 px wide; a hero that fills a phone screen is a small part of it. Judge on the mobile run.
  • Chasing the last 200 ms of FCP while TTFB is 1.2 s. The server sets the floor; page caching can move FCP by a second in one setting.

Questions people ask

What is First Contentful Paint?

First Contentful Paint is the time from the start of navigation until the browser first draws any text, image, SVG or non-blank canvas. Google rates it good at 1.8 s or less and poor above 3 s. It shows how long a visitor stares at a blank screen, and it is limited by the server response, redirects, render-blocking CSS and JavaScript, and web fonts that hide text while they load.

What is the difference between Speed Index and Largest Contentful Paint?

Largest Contentful Paint is one moment: when the biggest image or text block in the viewport is drawn. Speed Index is an average over the whole load: how quickly the visible area as a whole looks finished. A page with one late hero has a poor LCP and a fair Speed Index; a page where many small parts arrive late has the opposite. LCP is a Core Web Vital; Speed Index is lab-only.

Does a loading spinner count as First Contentful Paint?

Yes, if it is an image, SVG or text, a spinner counts as contentful, so a page that shows one early gets a good FCP even though nothing useful is on screen. That is why Google made Largest Contentful Paint the loading Core Web Vital instead. If your FCP is fast but LCP and Speed Index are slow, look for a spinner or skeleton covering content rendered by JavaScript.

Check your site before and after Check