First Contentful Paint: what it measures and what moves it

You fix First Contentful Paint by reducing back end wait, unblocking the first render and sorting fonts. This guide shows what FCP measures, why it sits in Lighthouse, how it relates to LCP, and a quick routine you can run on any page.

By , founder of Porteur · Updated 14 September 2026 · Markdown

What First Contentful Paint measures

First Contentful Paint is the time from navigation to the first text or image draw. It is the moment something real appears, not a blank screen.

  • Good as of 2026: up to 1.8 s
  • Needs work: 1.8 s to 3 s
  • Poor: above 3 s

FCP is not a Core Web Vital. It is part of Lighthouse’s Performance score, weighted 10 percent. It does not appear in the Search Console Core Web Vitals report.

You will see two kinds of data. Field data, from real Chrome users over 28 days at the 75th percentile. Lab data, a single Lighthouse run on a simulated mid-range phone over throttled 4G. The two can disagree. Field data wins for Search and for prioritisation.

Why FCP matters on a small site

Users feel the page has started when FCP lands early. It reduces the blank screen time while the rest builds. It sets the tone before LCP arrives.

You do not chase 100. You ship fast first paint on the pages that get visits: the home page, /pricing, /guides/getting-started. A quick first draw lowers drop-offs on slow mobile connections and older devices.

How FCP relates to LCP, INP and CLS

FCP is the first visible content. LCP is the largest above-the-fold content: a hero image, a big heading or a video poster. FCP is first, LCP is the big thing you care about for rankings.

MetricWhat it measuresGood threshold
Largest Contentful Paint (Core Web Vital)Time to the largest image, video poster or text block in the viewportUp to 2.5 s
Cumulative Layout Shift (Core Web Vital)Unexpected layout movement during a session windowUp to 0.1
Interaction to Next Paint (Core Web Vital)Slowest interaction’s delay to next paint over a visitUp to 200 ms
First Contentful Paint (Lighthouse only)Time to first text or image drawUp to 1.8 s
Time to First ByteTime to first response byte from the serverUp to 0.8 s

INP replaced First Input Delay in March 2024. Lighthouse cannot interact, so Total Blocking Time is its lab proxy for INP and carries 30 percent of the score. Keep that in mind when you balance work.

What actually moves FCP

Three levers move FCP on most product sites: back end wait, render-blocking resources, and fonts. Fix them in this order.

  • Time to First Byte: long server work or slow networks delay the first paint.
  • Render-blocking resources: CSS in the head blocks rendering, synchronous scripts block parsing.
  • Fonts: the first text cannot draw until the font decision is clear, and a late web font can blank text.

A quick diagnosis routine for any page

  1. Open PageSpeed Insights for your URL

    Check the Mobile tab first. Look at the field section at the top. If FCP shows in the distribution, note the 75th percentile. If there is no field data, use Lighthouse lower down but treat it as lab only.

  2. Compare with LCP and TTFB

    If LCP is slow and TTFB is high, start with the server. If FCP is slow but TTFB is fine, look for blocking CSS and fonts.

  3. Read Lighthouse opportunities

    Under Diagnose performance issues, expand Render-blocking resources, Reduce unused CSS and Eliminate render-blocking resources. Expand Preload key requests and Ensure text remains visible during webfont load.

  4. Inspect the head of the document

    View-source on yourpage.com. Count stylesheets and synchronous scripts in the head. Note @import in CSS. Spot any Google Fonts CSS URL and whether you preload fonts.

  5. Check the first paint content

    What draws first? A logo SVG, a nav, or a heading. Make sure the markup for that content is in the HTML, not injected by JavaScript.

  6. Run a local throttle test

    Use Chrome DevTools, Performance panel. Simulate a mid-range phone and slow 4G. Record once. Look at the first paint and long tasks around it.

A page that is fixed will show FCP close to TTFB plus a small render time, stable across runs on mobile. The first text appears before any heavy scripts execute.

Reduce Time to First Byte

TTFB is the time from request start to the first byte of the response. It includes DNS, TCP and TLS, redirects, and your server’s work. It sets the earliest possible FCP.

  • Cache or pre-render the HTML on the edge. Serve the shell fast even if data loads later.
  • Use a CDN close to users. Enable HTTP/2 or HTTP/3 and keep-alive.
  • Remove redirect chains. Send the final URL in all links and sitemaps.
  • Profile slow back end code. Cut database round trips and serial API calls.
  • Ship 103 Early Hints for critical CSS and fonts when your stack supports it.

On yourproduct.com/pricing, a drop in TTFB from 1.6 s to 0.5 s often moves FCP by about a second, because the browser can start layout and paint sooner.

Unblock the first render

Stylesheets in the head block first paint. Synchronous scripts block parsing. Your goal is to inline only what is needed for the above-the-fold view, and defer the rest without breaking the design.

  • Inline critical CSS for the hero and nav. Under about 14 kB compressed is a common ceiling for the first chunk.
  • Load non-critical CSS with media=print and swap onload, or use disabled until onload patterns. Remove @import chains.
  • Defer or async all scripts that do not set up the first paint. type=module defers by default. Move any remaining blocking scripts to the end of body.
  • Remove unused CSS. Ship fewer kilobytes and fewer files. A single merged CSS file for critical styles is fine.
  • Avoid JavaScript-inserted first content. Server-render the first heading and nav so the browser can paint without waiting for hydration.

/guides/getting-started with four CSS files in the head and a synchronous analytics script will paint late. Inline the header styles, defer the script, and load the rest of the CSS after onload. The first H1 will draw sooner, and FCP will drop under 1.8 s on mobile in lab runs.

Make fonts stop blocking paint

Web fonts delay text. The browser must decide which face to use and fetch it. If the font arrives late and you block rendering, FCP suffers. If you swap too late, you get a flash of invisible text.

  • Use font-display: swap or optional so fallback text draws at FCP. Only the first viewport needs this guarantee.
  • Preload the one font file used above the fold. rel=preload as=font crossorigin. Point it at the exact WOFF2 file.
  • Self-host fonts in WOFF2. A third-party host adds DNS, connection and TLS before bytes flow.
  • Subset by unicode-range so the file the first paint needs is small. A variable font can replace several files, but still subset.
  • Consider a system font stack for the UI. It costs no network trip and paints early.
  • If the swapped font shifts layout, match metrics. Use size-adjust and ascent-override on the fallback to keep CLS low.

On yourproduct.com, if your first visible copy is the H1, preload the regular weight that renders it. Set font-display: swap. The first paint will not wait for a 90 kB font from a third-party CSS URL.

Do third-party scripts affect FCP

They can. A synchronous tag in the head blocks parsing. Even deferred scripts compete for bandwidth before the first paint if you start many at once. A heavy tag manager container can inject CSS and JS that add work before paint.

  • Remove scripts nobody uses. Verify in your analytics and tag manager.
  • Load on interaction when possible. Use a facade for video embeds.
  • Defer until after load. Preconnect to origins that must start early, but be sparing.
  • Keep the tag manager container small and split by environment.

How to check FCP the right way

PageSpeed Insights shows field data at the top when there is enough traffic, and a Lighthouse run below. Scores vary between runs. Use field data to judge real users and pass or fail of Core Web Vitals. Use Lighthouse to find fixes. The Lighthouse score itself is not a ranking factor.

  • Search Console’s Core Web Vitals report reads field data per URL or origin. It shows LCP, CLS and INP only.
  • CrUX field data comes from opted-in Chrome users on Android and desktop, not iOS.
  • Mobile-first indexing is complete. Prioritise mobile FCP and LCP.

A worked example: moving a slow FCP

Say /pricing has FCP 3.2 s on Mobile in Lighthouse, TTFB 1.4 s, LCP 4.6 s. You see three CSS files in the head, a Google Fonts stylesheet, and a synchronous A/B test script. The hero image is marked loading=lazy.

  1. Fix the server wait

    Enable HTML caching for /pricing and edge cache it. TTFB falls to 0.6 s.

  2. Inline critical CSS

    Inline styles for the header, hero and first section. Load the rest after onload. Remove @import chains. Fewer requests hit the head.

  3. Defer scripts

    Move the A/B test off the critical path or run it as deferred with a fast no-flash variation CSS. Defer analytics too.

  4. Sort fonts

    Self-host the Inter regular WOFF2, preload it, and set font-display: swap. Add size-adjust to match fallback.

  5. Fix the hero

    Remove loading=lazy and add fetchpriority=high to the hero image. If it is a CSS background, add a preload link.

After these changes the next Lighthouse run shows FCP about 1.6 s and LCP about 2.5 s. Field data will follow if traffic is steady, as it needs 28 days and the 75th percentile to move.

Questions

Sources

Check my site, free

Paste your URL to see how your first paint stacks up against rivals and which of TTFB, blocking CSS or fonts holds it back, in about thirty seconds, free.

  • Free check, no card
  • Read-only, your own accounts
  • Readable by your agent

Read next