Critical CSS is the technique of identifying and inlining the minimum CSS required to render the above-the-fold content of a page. The remaining styles are loaded asynchronously after the initial render.
The goal is to eliminate render-blocking CSS requests. When a browser encounters a <link rel="stylesheet">, it pauses rendering until the stylesheet is downloaded and parsed. By inlining critical styles directly in the <head>, the page renders immediately, dramatically improving perceived performance and Core Web Vitals scores.
"Above the fold" refers to the portion of the page visible without scrolling. The exact content varies by device — a 1920px desktop screen shows more above the fold than a 375px mobile phone. Critical CSS must account for multiple viewport sizes.
Include critical styles for mobile, tablet, and desktop in your inlined CSS. A common mistake is only optimizing for one viewport — the Lighthouse score on other devices will suffer.
Extracting Critical CSS
Manual extraction does not scale for real projects. Automated tools analyze your rendered HTML against your CSS to determine which rules apply above the fold. The process typically involves:
◆Step 1: Load the page in a headless browser (Puppeteer/Playwright)
◆Step 2: Set viewport sizes for mobile, tablet, and desktop
◆Step 3: Extract all computed styles for visible elements only
◆Step 4: Deduplicate and minify the extracted CSS
◆Step 5: Inline the result in the HTML head
extract-critical.js
JavaScript
1
// Critical CSS extraction with Puppeteer
2
const puppeteer = require('puppeteer');
3
const { critical } = require('critical');
4
5
asyncfunction generateCriticalCSS(url) {
6
const result = await critical.generate({
7
base: '/path/to/site',
8
src: url,
9
targets: {
10
critical: 'critical.css',
11
stylesheet: 'styles.css', // remaining styles
12
},
13
width: [375, 768, 1280], // viewports to target
14
height: [667, 1024, 800],
15
inline: true,// inline critical CSS
16
minify: true,
17
});
18
19
return result;
20
}
21
22
// Programmatic usage
23
critical.generate({
24
inline: true,
25
base: 'dist/',
26
src: 'index.html',
27
target: 'index-critical.html',
28
width: [375, 768, 1280],
29
height: [667, 1024, 800],
30
});
Inline Critical CSS
Inlined CSS is embedded directly in the <style> tag within the <head>. This eliminates network latency for the initial render — the browser has the styles immediately.
inline-critical.html
HTML
1
<head>
2
<!-- Inlined critical CSS — no network request needed -->
3
<style>
4
/* Critical styles for hero, nav, above-fold content */
Keep inlined critical CSS under 14KB (compressed) to avoid exceeding the initial TCP congestion window. Above 14KB, the browser needs an extra round trip, negating some of the performance benefit.
Loading Non-Critical CSS Async
The remaining CSS should be loaded without blocking the initial render. Several techniques achieve this, each with different trade-offs.
Preload + onload
preload-onload.html
HTML
1
<!-- Most common technique — preload, then swap -->
Hero images and headings style immediately, no flash
TBT
Total Blocking Time (main thread)
Reduced CSS parsing overhead on the main thread
CLS
Cumulative Layout Shift
Proper critical CSS prevents layout shifts from async CSS
ℹ
info
A typical improvement from implementing critical CSS: FCP drops from 2.5s to 1.0s, and LCP from 4.0s to 1.8s on 3G connections. This alone can move a site from "needs improvement" to "good" on Core Web Vitals.
Code Splitting CSS
Critical CSS pairs naturally with CSS code splitting. Rather than one monolithic stylesheet, split CSS by route, component, or feature, and load each chunk only when needed.