TL;DR: CSS blocks rendering by default. Critical CSS inlining extracts above-fold styles into the
<head>, loads full CSS async, and eliminates render-blocking requests. This guide covers extraction tools, inlining thresholds, and async loading patterns. Applied at tanstackship.com.
Introduction
Every external CSS file adds a round trip that blocks page rendering. Inlining critical CSS — the styles needed for initial viewport content — eliminates these round trips for the first paint, dramatically improving Largest Contentful Paint.
How Critical CSS Works
| Approach | First Paint | Implementation Complexity | Maintenance |
|---|---|---|---|
Full CSS in <head> | Instant | None (inline all) | CSS cannot be cached |
| Extracted critical CSS | Near-instant | Medium | Requires build step |
| HTTP/2 server push | Fast | Low | Deprecated in Chrome |
| Inline + async full CSS | Near-instant | Medium | Best balance |
| Critical CSS + preload | Instant | High | Optimal production setup |
Critical CSS Extraction
// Build-time extraction with critical package
import { generate } from 'critical'
await generate({
base: 'dist/',
src: 'index.html',
target: 'index.html',
inline: true,
width: 1440,
height: 900,
extract: true,
})
This generates an index.html with critical styles inlined and the full CSS loaded asynchronously.
Async CSS Loading Pattern
<!-- Critical CSS inlined in <head> -->
<style>
/* ~15KB of above-fold styles */
header, nav, .hero { ... }
</style>
<!-- Full CSS loaded async -->
<link rel="preload" href="/assets/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/styles.css"></noscript>
Production Implementation
// TanStack Start: critical CSS middleware
import { defineMiddleware } from '~/lib/middleware'
export const criticalCSSMiddleware = defineMiddleware({
async after({ response }) {
if (response.headers.get('Content-Type')?.includes('text/html')) {
const html = await response.text()
const criticalCSS = extractCriticalCSS(html)
const optimized = html.replace(
'</head>',
`<style id="critical-css">${criticalCSS}</style>
<link rel="preload" href="/assets/styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/assets/styles.css"></noscript>
</head>`
)
return new Response(optimized, response)
}
}
})
Inlining Threshold Guidelines
| Scenario | Max Inline CSS Size | Impact |
|---|---|---|
| Mobile (3G) | 10-15 KB | Saves 300-800ms |
| Desktop (WiFi) | 20-30 KB | Saves 100-300ms |
| First visit | Smaller is better | Largest gain |
| Repeat visit | Defer to cache | Less impact |
Conclusion
Critical CSS inlining is one of the highest-ROI performance optimizations. Extract above-fold styles at build time, inline them in <head>, and load the full stylesheet asynchronously. This eliminates render-blocking CSS requests and delivers near-instant first paint.
For complementary performance patterns, see our Font Loading Strategies for Web Apps and the Preload, Prefetch, and Preconnect Guide — together they cover the full critical rendering path. Combine with Reducing Time to First Byte (TTFB) and the Core Web Vitals Optimization Guide for a comprehensive performance strategy.