How to Prioritize Critical CSS
Critical CSS is the minimum CSS needed to render above-the-fold content. Inlining it eliminates render-blocking and dramatically improves FCP.
Every CSS file in your <head> is render-blocking — the browser won't paint a single pixel until all CSS is downloaded and parsed. Critical CSS solves this by inlining only the styles needed for above-the-fold content directly into the HTML.
The Problem: Render-Blocking CSS
Typical page load with external CSS:
1. Download HTML (200ms)
2. Parse HTML, discover CSS link (0ms)
3. Download CSS file (300ms) ← BLOCKED
4. Parse CSS (50ms) ← BLOCKED
5. First paint (550ms total)
With critical CSS inlined:
1. Download HTML with inline critical CSS (220ms)
2. First paint (220ms total) ← 60% faster!
3. Load remaining CSS asynchronously (non-blocking)
What Is Critical CSS?
Critical CSS is the subset of your stylesheet that's needed to render the initial viewport (above-the-fold content). Everything below the fold can be loaded later.
For a typical page, critical CSS might be:
- Layout (grid/flex for the header and hero)
- Typography (font sizes, colors for visible text)
- Navigation styles
- Hero section styles
- Background colors
Extracting Critical CSS
Automated Tools
Critical (by Addy Osmani)
npm install critical
const critical = require('critical');
critical.generate({
base: 'dist/',
src: 'index.html',
css: ['dist/styles.css'],
width: 1300,
height: 900,
inline: true,
target: 'index-critical.html',
});
Critters (Webpack/Vite plugin)
npm install critters-webpack-plugin
Critters inlines critical CSS at build time automatically.
PurgeCSS + Manual Extraction For more control, use PurgeCSS to remove unused CSS, then manually identify above-fold styles.
Manual Approach
- Open Chrome DevTools → Coverage tab
- Load the page and see which CSS rules are used
- Copy the used CSS for above-fold elements
- Inline it in
<style>tags in the<head>
Implementation
Step 1: Inline Critical CSS
<head>
<!-- Critical CSS inlined -->
<style>
/* Only styles needed for above-fold content */
body { margin: 0; font-family: system-ui; background: #0f172a; color: #e2e8f0; }
.nav { display: flex; height: 56px; align-items: center; padding: 0 24px; }
.hero { padding: 64px 24px; text-align: center; }
.hero h1 { font-size: 3rem; font-weight: 900; }
</style>
<!-- Full CSS loaded asynchronously -->
<link rel="preload" href="/styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>
</head>
Step 2: Async Load Remaining CSS
The preload trick above loads the full stylesheet without blocking rendering. When it finishes, it swaps to rel="stylesheet".
Step 3: Framework Integration
Next.js already extracts and inlines critical CSS automatically when using CSS Modules or styled-jsx.
Vite + Critters:
// vite.config.js
import critters from 'critters';
export default {
plugins: [critters()],
};
Measuring the Impact
| Metric | Before | After Critical CSS |
|---|---|---|
| FCP | 2.5s | 1.0s |
| LCP | 3.5s | 2.0s |
| Render-blocking time | 500ms | 0ms |
| Lighthouse | +10-20 points | — |
Common Pitfalls
- Critical CSS too large — keep it under 14KB (one TCP round trip). If it's larger, you're including too many styles.
- Flash of unstyled content (FOUC) — ensure critical CSS covers all visible elements, not just some.
- Duplication — critical CSS is duplicated (inline + in the full stylesheet). Accept this trade-off for performance.
- Dynamic content — if above-fold content changes (A/B tests, personalization), your critical CSS must account for all variants.
Monitor Rendering Performance
Render-blocking resources are one of the most common performance issues. BadPageSpeed tracks your Core Web Vitals including FCP.
Ready to stop wasting ad spend?
Track your landing page performance, monitor Core Web Vitals, and calculate exactly how much slow pages cost you.
Start Free — No Credit Card