OtherAdvanced

Critical CSS Setup Template

A step-by-step template for extracting, inlining, and managing critical CSS. Includes build tool configurations for webpack, Vite, Next.js, and manual approaches.

Time to Complete
1-3 hours
Word Count
1,500-3,000 words
Sections
6
Difficulty
Advanced

Best Used For

New Project Critical CSS

Set up critical CSS extraction from the start in a new web project

Performance Retrofit

Add critical CSS to an existing site to eliminate render-blocking stylesheets

Build Pipeline Integration

Automate critical CSS extraction in your CI/CD pipeline

Template Structure

1

Identify Critical CSS

Determine which CSS rules are needed for above-the-fold content rendering

Example: Use Chrome Coverage tab to find above-fold CSS rules
2

Extract Critical CSS

Use automated tools to extract the minimum CSS for initial render

Example: Critters, Critical, PurgeCSS configurations
3

Inline Critical CSS

Embed extracted CSS in the HTML <head> for zero round-trip delivery

Example: <style> block in <head> with critical rules
4

Async Load Remaining CSS

Load the full stylesheet without blocking rendering

Example: media='print' onload pattern for non-blocking CSS
5

Build Tool Configuration

Configure webpack, Vite, or Next.js to automate critical CSS extraction

Example: Critters webpack plugin, Vite plugin, Next.js config
6

Validation and Testing

Verify critical CSS works correctly and doesn't cause FOUC

Example: Lighthouse audit, visual regression testing, slow network simulation

Example Outputs

Identify Critical CSS

Use Chrome Coverage tab to find above-fold CSS rules

Extract Critical CSS

Critters, Critical, PurgeCSS configurations

Inline Critical CSS

<style> block in <head> with critical rules

Async Load Remaining CSS

media='print' onload pattern for non-blocking CSS

Build Tool Configuration

Critters webpack plugin, Vite plugin, Next.js config

Common Pitfalls

  • Critical CSS should be under 14KB to fit in the first TCP round trip
  • Always include a noscript fallback for the deferred stylesheet
  • Test on throttled mobile connections to verify above-fold rendering
  • Use content-hashed filenames for the deferred full stylesheet
  • Faster FCP from inlined critical CSS improves Core Web Vitals scores
  • Reduced render-blocking improves crawl efficiency for search engines

Optimization Tips

SEO Tips

  • Critical CSS should be under 14KB to fit in the first TCP round trip
  • Always include a noscript fallback for the deferred stylesheet
  • Test on throttled mobile connections to verify above-fold rendering
  • Use content-hashed filenames for the deferred full stylesheet

GEO Tips

  • Faster FCP from inlined critical CSS improves Core Web Vitals scores
  • Reduced render-blocking improves crawl efficiency for search engines

Example Keywords

critical css setupinline critical cssextract critical css webpackeliminate render blocking css

What This Template Covers

Critical CSS is the minimum CSS required to render above-the-fold content without a Flash of Unstyled Content (FOUC). By inlining it in the HTML <head>, you eliminate render-blocking stylesheet requests and cut First Contentful Paint by 200-500ms on typical sites.

This template provides working configurations for the most common build tools and frameworks.


Section 1: Identify Critical CSS

Manual identification

  1. Open Chrome DevTools → Coverage tab (Ctrl+Shift+P → "Show Coverage")
  2. Reload the page
  3. Scroll to the first viewport boundary (above-fold content)
  4. Click on CSS files in the Coverage report
  5. Blue-highlighted rules are used; red are unused on this viewport

Above-fold typically includes: header, navigation, hero section, and the first content block.

Automated identification

Tools like Penthouse and Critical analyze a page at a specific viewport size and extract only the CSS needed:

# Using the critical npm package
npx critical https://yoursite.com --base ./dist --inline

Size target

Critical CSS should be under 14KB (compressed). This is the maximum payload that fits in the first TCP congestion window, meaning it arrives in a single round trip. Larger critical CSS requires additional round trips, reducing the benefit.


Section 2: Extract Critical CSS

Using the critical npm package

// scripts/extract-critical.js
const critical = require("critical")

critical.generate({
  base: "dist/",
  src: "index.html",
  css: ["dist/styles.css"],
  width: 1300,
  height: 900,
  inline: true,
  target: {
    html: "dist/index.html",
    css: "dist/critical.css"
  }
})

Using Penthouse (more control)

const penthouse = require("penthouse")

async function extractCritical() {
  const criticalCss = await penthouse({
    url: "http://localhost:3000",
    cssString: fullCss,
    width: 1300,
    height: 900,
    forceInclude: [".header", ".nav", ".hero"]
  })

  return criticalCss
}

The forceInclude option ensures specific selectors are always included, even if the headless browser doesn't detect them as above-fold (useful for interactive elements that appear on hover/click).


Section 3: Inline Critical CSS

HTML structure

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Page Title</title>

    <!-- Critical CSS: inlined for instant rendering -->
    <style>
      :root {
        --bg: #fff;
        --text: #111;
        --primary: #2563eb;
      }
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        font-family: system-ui, sans-serif;
        color: var(--text);
        background: var(--bg);
      }
      .header {
        height: 64px;
        display: flex;
        align-items: center;
        padding: 0 24px;
        border-bottom: 1px solid #eee;
      }
      .hero {
        min-height: 60vh;
        display: grid;
        place-items: center;
        padding: 48px 24px;
      }
      h1 {
        font-size: clamp(2rem, 5vw, 3.5rem);
        line-height: 1.1;
        max-width: 800px;
      }
    </style>

    <!-- Full stylesheet: loaded without blocking render -->
    <link
      rel="stylesheet"
      href="/styles.a1b2c3.css"
      media="print"
      onload="this.media='all'"
    />
    <noscript><link rel="stylesheet" href="/styles.a1b2c3.css" /></noscript>
  </head>
  <body>
    <!-- Page content -->
  </body>
</html>

How the async loading works

  1. media="print" — Browser downloads the stylesheet but doesn't apply it (print-only)
  2. onload="this.media='all'" — When download completes, switch to apply for all media
  3. <noscript> fallback — Loads stylesheet normally for users with JavaScript disabled

This pattern is recommended by Google and supported by all modern browsers.


Section 4: Build Tool Configurations

webpack with Critters

// webpack.config.js
const Critters = require("critters-webpack-plugin")

module.exports = {
  plugins: [
    new Critters({
      // Inline critical CSS, async load the rest
      preload: "swap",
      // Include CSS for these selectors even if not detected
      additionalStylesheets: [],
      // Don't inline styles larger than 20KB
      inlineThreshold: 20000
    })
  ]
}

Critters runs at build time:

  1. Renders each HTML page
  2. Identifies CSS used by visible elements
  3. Inlines critical CSS as <style> tags
  4. Converts <link> to async loading

Vite with vite-plugin-critical

// vite.config.js
import critical from "vite-plugin-critical"

export default {
  plugins: [
    critical({
      criticalUrl: "http://localhost:5173",
      criticalBase: "./dist",
      criticalPages: [
        { uri: "/", template: "index" },
        { uri: "/about", template: "about" }
      ],
      criticalConfig: {
        width: 1300,
        height: 900
      }
    })
  ]
}

Next.js

Next.js includes built-in CSS optimization:

// next.config.js
module.exports = {
  experimental: {
    optimizeCss: true // Uses Critters internally
  }
}

For more control, use next/script to load non-critical CSS:

import Script from "next/script"

// In your layout
;<Script id="load-extra-css" strategy="afterInteractive">
  {`
    var link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = '/extra-styles.css';
    document.head.appendChild(link);
  `}
</Script>

Section 5: Validation and Testing

Lighthouse audit

Run Lighthouse and check:

  • "Eliminate render-blocking resources" — should show no CSS files (they're now inlined or async)
  • First Contentful Paint — should improve by 200-500ms
  • Largest Contentful Paint — should improve if LCP element was delayed by CSS

Visual regression testing

Critical CSS extraction can miss selectors, causing FOUC. Test by:

  1. Disable JavaScript in browser settings
  2. Load the page — above-fold content should be fully styled from the inlined CSS
  3. Compare against the fully-styled version

Slow network simulation

  1. DevTools → Network → Slow 3G
  2. Reload the page
  3. Above-fold content should render immediately (from inlined CSS)
  4. Full styles should apply as the async stylesheet loads

Automated CI check

# Verify critical CSS is present in HTML
curl -s https://yoursite.com | grep -q '<style>' && echo "Critical CSS found" || echo "MISSING"

# Verify no render-blocking CSS in head
curl -s https://yoursite.com | grep '<link rel="stylesheet"' | grep -v 'media="print"' && echo "WARNING: blocking CSS found"

Maintaining Critical CSS

Per-page vs. shared critical CSS

  • Per-page: Each page type has its own critical CSS (most precise, best performance)
  • Shared: One critical CSS covers all page templates (simpler, slightly larger)

For content sites with a consistent layout, shared critical CSS is practical. For sites with diverse page layouts (marketing + dashboard + checkout), per-page extraction is worth the complexity.

Updating critical CSS

Re-extract critical CSS when:

  • Page layout changes (new header, redesigned hero)
  • Global CSS changes (new CSS variables, updated base styles)
  • New page templates are added

Automate extraction in your CI pipeline to keep critical CSS in sync with layout changes.


Frequently Asked Questions

How much performance improvement should I expect?

Sites with large CSS bundles (150KB+) typically see a 200-500ms FCP improvement. Sites with already-small CSS (< 30KB) see smaller gains. The improvement is most noticeable on mobile connections.

Can critical CSS cause FOUC?

If extracted incorrectly, yes. If the critical CSS misses important above-fold rules, those elements appear unstyled until the full stylesheet loads. Always validate by testing with JavaScript disabled and slow network conditions.

Should I inline critical CSS for every page?

For pages where performance matters (landing pages, key content pages, e-commerce product pages), yes. For low-traffic admin pages, the complexity may not be worth it.

Does critical CSS work with CSS-in-JS?

For libraries with server-side extraction (styled-components with SSR, Emotion with SSR), critical CSS is handled by the SSR process. For runtime-only CSS-in-JS, you need a separate critical CSS tool or should migrate to static CSS extraction.

Generate Content with This Template

Rankwise uses this template structure automatically. Create AI-optimized content in minutes instead of hours.

Try Rankwise Free
Newsletter

Stay ahead of AI search

Weekly insights on GEO and content optimization.