Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Optimize first contentful paint

First content renders within 1.8 seconds, providing quick visual feedback that the page is loading.

Utilities
Quick take
Typical fix time 20 min
  • FCP measures time until first text/image appears—target under 1.8s
  • Eliminate render-blocking CSS and JavaScript
  • Inline critical CSS for above-fold content
  • Use preconnect for third-party origins
Why it matters: FCP is the first visual signal that a page is loading—fast FCP reassures users that the site is responding, reducing perceived wait time and bounce rates.

Rule Details

First Contentful Paint marks when the browser renders the first piece of content.

Code Example

HTML → DOM → CSSOM → Render Tree → Paint
 
Blocking resources delay this path:
- Synchronous JavaScript
- Render-blocking CSS
- Large HTML documents

Why It Matters

FCP is the first visual signal that a page is loading—fast FCP reassures users that the site is responding, reducing perceived wait time and bounce rates.

FCP Score Thresholds

ScoreRatingUser Perception
0–1.8sGoodPage is responsive
1.8–3sNeeds improvementNoticeable delay
> 3sPoorUser may abandon

Eliminate Render-Blocking Resources

<!-- Bad: Render-blocking -->
<head>
  <link rel="stylesheet" href="/all-styles.css">
  <script src="/app.js"></script>
</head>
 
<!-- Good: Non-blocking -->
<head>
  <!-- Inline critical CSS -->
  <style>
    /* Critical above-fold styles */
    body { margin: 0; font-family: system-ui; }
    .header { height: 60px; background: #fff; }
    .hero { min-height: 400px; }
  </style>
 
  <!-- Defer non-critical CSS -->
  <link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/styles.css"></noscript>
 
  <!-- Defer JavaScript -->
  <script src="/app.js" defer></script>
</head>

Preconnect to Critical Origins

<head>
  <!-- Establish early connections -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link rel="preconnect" href="https://cdn.example.com" crossorigin>
 
  <!-- DNS prefetch for less critical origins -->
  <link rel="dns-prefetch" href="https://analytics.example.com">
</head>

Optimize Server Response Time

// Next.js - reduce TTFB with caching
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, s-maxage=3600, stale-while-revalidate=86400',
          },
        ],
      },
    ]
  },
}
// Use streaming for faster first byte
// app/page.tsx (Next.js App Router)
import { Suspense } from 'react'
 
export default function Page() {
  return (
    <main>
      {/* Static content renders immediately */}
      <Header />
      <Hero />
 
      {/* Dynamic content streams in */}
      <Suspense fallback={<ProductSkeleton />}>
        <Products />
      </Suspense>
    </main>
  )
}

Inline Critical CSS

// Build tool to extract and inline critical CSS
// Using critical package
const critical = require('critical')
 
critical.generate({
  base: 'dist/',
  src: 'index.html',
  target: 'index-critical.html',
  inline: true,
  width: 1300,
  height: 900,
  penthouse: {
    blockJSRequests: false,
  },
})

Font Loading Optimization

<!-- Preload critical fonts -->
<link
  rel="preload"
  href="/fonts/main.woff2"
  as="font"
  type="font/woff2"
  crossorigin
>
/* Use font-display to avoid blocking text */
@font-face {
  font-family: 'MainFont';
  src: url('/fonts/main.woff2') format('woff2');
  font-display: swap; /* Show fallback text immediately */
}

React Server Components

// Server components render faster - no client JS needed
// app/components/Hero.tsx
export default function Hero() {
  // This component renders on server, no hydration delay
  return (
    <section className="hero">
      <h1>Welcome to Our Site</h1>
      <p>Content visible immediately</p>
    </section>
  )
}
 
// app/page.tsx
import Hero from './components/Hero'
 
export default function Page() {
  return (
    <>
      <Hero /> {/* Fast FCP - server rendered */}
      <InteractiveSection /> {/* Client component, loads after */}
    </>
  )
}

Measuring FCP

// Using web-vitals library
import { onFCP } from 'web-vitals'
 
onFCP(metric => {
  console.log('FCP:', metric.value, 'ms')
 
  // Report to analytics
  if (metric.value > 1800) {
    console.warn('FCP exceeds 1.8s threshold')
  }
})
 
// Using Performance API
const observer = new PerformanceObserver(list => {
  for (const entry of list.getEntries()) {
    if (entry.name === 'first-contentful-paint') {
      console.log('FCP:', entry.startTime)
    }
  }
})
observer.observe({ type: 'paint', buffered: true })

Verification

Automated Checks

  • Run Lighthouse—check FCP in Performance section
  • Use Chrome DevTools Performance panel
  • Test with network throttling (Slow 3G)
  • Check PageSpeed Insights for field data
  • Use WebPageTest for detailed waterfall analysis

Manual Checks

  • Confirm the user-facing outcome manually on a throttled target device or browser profile so the optimization still improves the real experience.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Measure FCP using Lighthouse or PageSpeed Insights. Verify first content appears within 1.8 seconds.

Fix

Auto-fix issues

Optimize FCP by eliminating render-blocking resources, inlining critical CSS, and reducing server response time.

Explain

Learn more

Explain how FCP measures the time until the first text or image is painted, indicating page load has begun.

Review

Code review

Review the routes, assets, and loading behavior that affect Optimize first contentful paint. Flag exact files, requests, or rendering steps that add unnecessary network, CPU, or layout cost, and describe the measurement method used to confirm the issue.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

PageSpeed Insights
pagespeed.web.devTool

Rules that often go hand-in-hand with this one.

Optimize largest contentful paint

The largest content element loads within 2.5 seconds for a good user experience.

Performance
Optimize interaction to next paint

Page responds to user interactions within 200ms, ensuring good responsiveness.

Performance
Minimize cumulative layout shift

Page maintains visual stability with a CLS score below 0.1, preventing unexpected content shifts during load.

Performance

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385