Performance topic: Critical CSS extraction
Critical CSS is the minimal set of styles needed to render above-the-fold content. You inline it directly in <head> so first paint doesn't wait on a network round-trip for the full stylesheet, then load the rest of the CSS asynchronously. Tools (Critical, Penthouse, framework plugins) extract it per route; the trade-off is build complexity and keeping it in sync.
Critical CSS is the subset of your stylesheet required to render above-the-fold content — what the user sees before scrolling.
The problem it solves
CSS is render-blocking: the browser won't paint until it has the CSSOM. If your stylesheet is one big file, First Contentful Paint waits for the entire download — a full network round-trip — even though only a fraction of those rules affect the initial view.
The technique
- Extract the rules that apply to above-the-fold elements (for a given viewport/route).
- Inline that critical CSS directly in a
<style>block in the<head>— zero network cost, available immediately. - Load the full stylesheet asynchronously so it doesn't block the first paint:
``html <style>/* critical CSS inlined here */</style> <link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="/styles.css"></noscript> ``
Result: the browser paints the visible content almost immediately, then the rest of the styles arrive and apply for below-the-fold and interactive states.
How it's extracted
- Tools like
critical,penthouse,crittersrender the page in a headless browser at a target viewport and collect the CSS rules that matched visible elements. - Framework integration — Next.js (via
critters/beasties), Nuxt, Astro, and SSR setups can do this per route at build or request time. - Run it per route/template, since each page's above-the-fold content differs.
Trade-offs
- ✅ Big win for FCP/LCP, especially on slow networks and first visits.
- ❌ Build complexity — extraction must run in the pipeline and be re-run when styles change, or it drifts out of sync.
- ❌ Inlined CSS isn't cached separately — it ships with every HTML response (mitigated by HTML compression; usually small).
- ❌ Getting the "fold" right is fuzzy across device sizes — tools target a viewport, so pick a representative one or a few.
When it matters most
First-paint-sensitive pages: landing pages, marketing sites, content sites, anything where bounce rate or Core Web Vitals matter. For app shells behind a login it matters less.
Senior framing
The senior answer states the why crisply — CSS is render-blocking, so don't make FCP wait for CSS the user can't even see yet — and is honest about the cost: it's a build-pipeline concern that drifts if not automated, and it's per-route. Mentioning that modern frameworks automate it (so you rarely hand-roll it now) shows current awareness.
Follow-up questions
- •Why is the full stylesheet still loaded after the critical CSS?
- •What are the downsides of inlining CSS?
- •How do you keep extracted critical CSS in sync with the real styles?
Common mistakes
- •Extracting once and never re-running it as styles change.
- •Using one critical-CSS bundle for every route.
- •Inlining too much, bloating the HTML.
- •Forgetting the <noscript> fallback for the async load.
Edge cases
- •The 'fold' varies by device — extraction targets a chosen viewport.
- •Inlined CSS isn't independently cacheable across pages.
- •Dynamic/interactive-state CSS isn't 'critical' and can load later.
Real-world examples
- •Landing pages, e-commerce home/category pages, content/news sites optimizing LCP.