[{"data":1,"prerenderedAt":1015},["ShallowReactive",2],{"content:\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time":3,"surroundings:\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time":1007},{"id":4,"title":5,"body":6,"description":987,"extension":988,"meta":989,"navigation":1000,"path":1001,"seo":1002,"stem":1005,"__hash__":1006},"content\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time\u002Findex.md","Extracting Critical CSS at Build Time",{"type":7,"value":8,"toc":970},"minimark",[9,14,29,37,150,155,184,188,194,200,206,212,216,221,235,239,462,466,469,473,482,601,605,608,680,684,690,779,783,786,790,816,820,826,840,850,859,863,875,884,893,902,911,920,929,933,955,960,963,966],[10,11,13],"h1",{"id":12},"how-to-extract-critical-css-at-build-time","How to Extract Critical CSS at Build Time",[15,16,17,18,23,24,28],"p",{},"This guide is part of ",[19,20,22],"a",{"href":21},"\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002F","Critical CSS & Render-Blocking Resources",", within ",[19,25,27],{"href":26},"\u002Frendering-css-performance\u002F","Rendering & CSS Performance",". Critical CSS — the small set of rules needed to render the first viewport — is the most effective way to remove render-blocking CSS from first paint. It is also easy to get wrong when maintained by hand: a designer adjusts the header, the hand-written critical block does not change, and suddenly the first paint shows a broken header that snaps into place when the full stylesheet loads.",[15,30,31,32,36],{},"The solution is to generate critical CSS in the build. Tools such as ",[33,34,35],"code",{},"critical"," (which renders pages in a headless browser at chosen viewports and keeps rules that match visible elements) and Beasties (formerly Critters, which works on static HTML without a browser and inlines rules used anywhere in the document) automate extraction and inlining. The key decisions are which tool to use, which viewports to cover, how to handle multiple templates, and how to verify the result.",[15,38,39],{},[40,41,47,48,47,55,47,59,47,62,47,71,47,77,47,86,47,92,47,97,47,102,47,105,47,108,47,111,47,114,47,117,47,120,47,125,47,129,47,131,47,135,47,137,47,140,47,142,47,145,47,147,47],"svg",{"viewBox":42,"width":43,"role":44,"ariaLabel":45,"style":46},"0 0 760 228","100%","img","Comparison of a headless-browser critical CSS extractor with a static HTML analyser.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[49,50],"rect",{"className":51,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],"svg-canvas","0","#ffffff",[56,57,58],"title",{},"critical vs Beasties (Critters)",[60,61,45],"desc",{},[49,63],{"x":64,"y":64,"width":65,"height":66,"rx":67,"fill":68,"stroke":69,"style":70},"1","758","226","10","none","currentColor","stroke-opacity:0.18",[72,73,58],"text",{"x":74,"y":75,"fill":69,"style":76},"28.0","34.0","font-size:16px;font-weight:700",[49,78],{"x":74,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":84,"style":85},"56.0","340.0","150.0","6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[72,87,91],{"x":88,"y":89,"fill":69,"style":90},"42.0","82.0","font-size:14px;font-weight:700","critical (headless browser)",[72,93,96],{"x":88,"y":94,"fill":69,"style":95},"108.0","font-size:12.5px;font-weight:700","•",[72,98,101],{"x":79,"y":94,"fill":69,"style":99,"textAnchor":100},"font-size:12.5px","start","Renders pages at chosen viewports",[72,103,96],{"x":88,"y":104,"fill":69,"style":95},"132.0",[72,106,107],{"x":79,"y":104,"fill":69,"style":99,"textAnchor":100},"Keeps only above-the-fold rules",[72,109,96],{"x":88,"y":110,"fill":69,"style":95},"156.0",[72,112,113],{"x":79,"y":110,"fill":69,"style":99,"textAnchor":100},"Smaller output, slower builds",[72,115,96],{"x":88,"y":116,"fill":69,"style":95},"180.0",[72,118,119],{"x":79,"y":116,"fill":69,"style":99,"textAnchor":100},"Needs a browser in CI",[49,121],{"x":122,"y":79,"width":80,"height":81,"rx":82,"fill":123,"stroke":123,"style":124},"392.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[72,126,128],{"x":127,"y":89,"fill":69,"style":90},"406.0","Beasties \u002F Critters (static)",[72,130,96],{"x":127,"y":94,"fill":69,"style":95},[72,132,134],{"x":133,"y":94,"fill":69,"style":99,"textAnchor":100},"420.0","Parses HTML, no browser needed",[72,136,96],{"x":127,"y":104,"fill":69,"style":95},[72,138,139],{"x":133,"y":104,"fill":69,"style":99,"textAnchor":100},"Keeps rules used anywhere in the page",[72,141,96],{"x":127,"y":110,"fill":69,"style":95},[72,143,144],{"x":133,"y":110,"fill":69,"style":99,"textAnchor":100},"Larger output, fast builds",[72,146,96],{"x":127,"y":116,"fill":69,"style":95},[72,148,149],{"x":133,"y":116,"fill":69,"style":99,"textAnchor":100},"Easy framework integration",[151,152,154],"h2",{"id":153},"rapid-diagnosis","Rapid Diagnosis",[156,157,158,166,172,178],"ul",{},[159,160,161,165],"li",{},[162,163,164],"strong",{},"Check for render-blocking stylesheets"," in Lighthouse or the Network panel's render-blocking column.",[159,167,168,171],{},[162,169,170],{},"Look for an existing inline style block."," If it is hand-written, compare it with the current design; stale critical CSS is common.",[159,173,174,177],{},[162,175,176],{},"Check build output."," Static site generators produce HTML files that are ideal for build-time extraction; SSR apps need extraction per template at build or render time.",[159,179,180,183],{},[162,181,182],{},"Measure the full CSS size."," If it is very small (under 10KB compressed), inlining it entirely may be simpler than extracting.",[151,185,187],{"id":186},"root-cause-analysis","Root Cause Analysis",[15,189,190,193],{},[162,191,192],{},"1. Critical CSS is page-dependent."," Different templates have different above-the-fold content.",[15,195,196,199],{},[162,197,198],{},"2. Viewports differ."," Mobile and desktop first viewports contain different elements.",[15,201,202,205],{},[162,203,204],{},"3. Designs change."," Hand-maintained critical blocks fall out of date.",[15,207,208,211],{},[162,209,210],{},"4. Dynamic content."," Elements injected by JavaScript are not seen by static extraction.",[151,213,215],{"id":214},"step-by-step-resolution","Step-by-Step Resolution",[217,218,220],"h3",{"id":219},"_1-choose-the-tool-for-your-stack","1. Choose the tool for your stack",[15,222,223,224,226,227,230,231,234],{},"For static sites and SSG output, ",[33,225,35],{}," gives the smallest output. For frameworks, Beasties integrations exist (Nuxt's ",[33,228,229],{},"experimental.inlineSSRStyles"," and similar options, Angular's ",[33,232,233],{},"inlineCritical",", Astro integrations).",[217,236,238],{"id":237},"_2-generate-per-template-at-multiple-viewports","2. Generate per template at multiple viewports",[240,241,246],"pre",{"className":242,"code":243,"language":244,"meta":245,"style":245},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F scripts\u002Fcritical.mjs — run after the static build.\nimport { generate } from 'critical';\nconst templates = ['index.html', 'blog\u002Fexample-post\u002Findex.html', 'products\u002Fexample\u002Findex.html'];\nfor (const page of templates) {\n  await generate({\n    base: 'dist\u002F',\n    src: page,\n    target: { html: page },               \u002F\u002F overwrite with inlined critical CSS\n    inline: true,\n    extract: false,                        \u002F\u002F keep full CSS intact for async loading\n    dimensions: [{ width: 360, height: 740 }, { width: 1300, height: 900 }],\n    penthouse: { timeout: 60000 },\n  });\n}\n\u002F\u002F trade-off: running a headless browser per page is slow; generate per template\n\u002F\u002F and apply the result to all pages that share it, rather than every URL.\n","javascript","",[33,247,248,257,278,311,331,344,356,362,371,382,397,426,438,444,450,456],{"__ignoreMap":245},[249,250,253],"span",{"class":251,"line":252},"line",1,[249,254,256],{"class":255},"sjfSM","\u002F\u002F scripts\u002Fcritical.mjs — run after the static build.\n",[249,258,260,264,268,271,275],{"class":251,"line":259},2,[249,261,263],{"class":262},"sPARh","import",[249,265,267],{"class":266},"saISM"," { generate } ",[249,269,270],{"class":262},"from",[249,272,274],{"class":273},"sZ8jY"," 'critical'",[249,276,277],{"class":266},";\n",[249,279,281,284,288,291,294,297,300,303,305,308],{"class":251,"line":280},3,[249,282,283],{"class":262},"const",[249,285,287],{"class":286},"sPXB4"," templates",[249,289,290],{"class":262}," =",[249,292,293],{"class":266}," [",[249,295,296],{"class":273},"'index.html'",[249,298,299],{"class":266},", ",[249,301,302],{"class":273},"'blog\u002Fexample-post\u002Findex.html'",[249,304,299],{"class":266},[249,306,307],{"class":273},"'products\u002Fexample\u002Findex.html'",[249,309,310],{"class":266},"];\n",[249,312,314,317,320,322,325,328],{"class":251,"line":313},4,[249,315,316],{"class":262},"for",[249,318,319],{"class":266}," (",[249,321,283],{"class":262},[249,323,324],{"class":286}," page",[249,326,327],{"class":262}," of",[249,329,330],{"class":266}," templates) {\n",[249,332,334,337,341],{"class":251,"line":333},5,[249,335,336],{"class":262},"  await",[249,338,340],{"class":339},"smZ65"," generate",[249,342,343],{"class":266},"({\n",[249,345,347,350,353],{"class":251,"line":346},6,[249,348,349],{"class":266},"    base: ",[249,351,352],{"class":273},"'dist\u002F'",[249,354,355],{"class":266},",\n",[249,357,359],{"class":251,"line":358},7,[249,360,361],{"class":266},"    src: page,\n",[249,363,365,368],{"class":251,"line":364},8,[249,366,367],{"class":266},"    target: { html: page },               ",[249,369,370],{"class":255},"\u002F\u002F overwrite with inlined critical CSS\n",[249,372,374,377,380],{"class":251,"line":373},9,[249,375,376],{"class":266},"    inline: ",[249,378,379],{"class":286},"true",[249,381,355],{"class":266},[249,383,385,388,391,394],{"class":251,"line":384},10,[249,386,387],{"class":266},"    extract: ",[249,389,390],{"class":286},"false",[249,392,393],{"class":266},",                        ",[249,395,396],{"class":255},"\u002F\u002F keep full CSS intact for async loading\n",[249,398,400,403,406,409,412,415,418,420,423],{"class":251,"line":399},11,[249,401,402],{"class":266},"    dimensions: [{ width: ",[249,404,405],{"class":286},"360",[249,407,408],{"class":266},", height: ",[249,410,411],{"class":286},"740",[249,413,414],{"class":266}," }, { width: ",[249,416,417],{"class":286},"1300",[249,419,408],{"class":266},[249,421,422],{"class":286},"900",[249,424,425],{"class":266}," }],\n",[249,427,429,432,435],{"class":251,"line":428},12,[249,430,431],{"class":266},"    penthouse: { timeout: ",[249,433,434],{"class":286},"60000",[249,436,437],{"class":266}," },\n",[249,439,441],{"class":251,"line":440},13,[249,442,443],{"class":266},"  });\n",[249,445,447],{"class":251,"line":446},14,[249,448,449],{"class":266},"}\n",[249,451,453],{"class":251,"line":452},15,[249,454,455],{"class":255},"\u002F\u002F trade-off: running a headless browser per page is slow; generate per template\n",[249,457,459],{"class":251,"line":458},16,[249,460,461],{"class":255},"\u002F\u002F and apply the result to all pages that share it, rather than every URL.\n",[217,463,465],{"id":464},"_3-apply-per-template-output-to-all-pages-of-that-template","3. Apply per-template output to all pages of that template",[15,467,468],{},"For sites with thousands of pages, generate critical CSS for one representative page per template and inject it into every page of that template during the build.",[217,470,472],{"id":471},"_4-load-the-full-stylesheet-asynchronously","4. Load the full stylesheet asynchronously",[15,474,475,477,478,481],{},[33,476,35],{}," with ",[33,479,480],{},"inline: true"," rewrites stylesheet links to load asynchronously (media swap with a noscript fallback). Check the output HTML to confirm.",[15,483,484],{},[40,485,47,488,47,491,47,494,47,496,47,513,47,516,47,518,47,522,47,529,47,534,47,537,47,541,47,544,47,547,47,551,47,554,47,558,47,561,47,565,47,568,47,571,47,575,47,578,47,581,47,589,47,593,47,597,47],{"viewBox":486,"width":43,"role":44,"ariaLabel":487,"style":46},"0 0 760 149","Build steps from compiling CSS through generating per-template critical CSS to inlining and verifying output.",[49,489],{"className":490,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],[56,492,493],{},"Build pipeline with critical CSS extraction",[60,495,487],{},[497,498,499],"defs",{},[500,501,508],"marker",{"id":502,"viewBox":503,"refX":504,"refY":505,"markerWidth":506,"markerHeight":506,"orient":507},"fa86d61c19","0 0 10 10","9","5","7","auto-start-reverse",[509,510],"path",{"d":511,"fill":69,"style":512},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[49,514],{"x":64,"y":64,"width":65,"height":515,"rx":67,"fill":68,"stroke":69,"style":70},"147",[72,517,493],{"x":74,"y":75,"fill":69,"style":76},[49,519],{"x":74,"y":79,"width":520,"height":521,"rx":82,"fill":123,"stroke":123,"style":124},"108.8","71.0",[72,523,528],{"x":524,"y":525,"fill":69,"style":526,"textAnchor":527},"82.4","78.0","font-size:13px;font-weight:700","middle","Build CSS",[72,530,533],{"x":524,"y":531,"fill":69,"style":532,"textAnchor":527},"96.0","font-size:12px","compile + purge",[49,535],{"x":536,"y":79,"width":520,"height":521,"rx":82,"fill":123,"stroke":123,"style":124},"176.8",[72,538,540],{"x":539,"y":525,"fill":69,"style":526,"textAnchor":527},"231.2","Build HTML",[72,542,543],{"x":539,"y":531,"fill":69,"style":532,"textAnchor":527},"SSG or prerender",[49,545],{"x":546,"y":79,"width":520,"height":521,"rx":82,"fill":83,"stroke":84,"style":85},"325.6",[72,548,550],{"x":549,"y":525,"fill":69,"style":526,"textAnchor":527},"380.0","Extract",[72,552,553],{"x":549,"y":531,"fill":69,"style":532,"textAnchor":527},"per template, 2",[72,555,557],{"x":549,"y":556,"fill":69,"style":532,"textAnchor":527},"112.0","viewports",[49,559],{"x":560,"y":79,"width":520,"height":521,"rx":82,"fill":123,"stroke":123,"style":124},"474.4",[72,562,564],{"x":563,"y":525,"fill":69,"style":526,"textAnchor":527},"528.8","Inline",[72,566,567],{"x":563,"y":531,"fill":69,"style":532,"textAnchor":527},"+ async full CSS",[49,569],{"x":570,"y":79,"width":520,"height":521,"rx":82,"fill":123,"stroke":123,"style":124},"623.2",[72,572,574],{"x":573,"y":525,"fill":69,"style":526,"textAnchor":527},"677.6","Check",[72,576,577],{"x":573,"y":531,"fill":69,"style":532,"textAnchor":527},"size budget +",[72,579,580],{"x":573,"y":556,"fill":69,"style":532,"textAnchor":527},"screenshot",[251,582],{"x1":583,"y1":584,"x2":585,"y2":584,"stroke":69,"strokeWidth":586,"style":587,"markerEnd":588},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa86d61c19)",[251,590],{"x1":591,"y1":584,"x2":592,"y2":584,"stroke":69,"strokeWidth":586,"style":587,"markerEnd":588},"285.6","323.6",[251,594],{"x1":595,"y1":584,"x2":596,"y2":584,"stroke":69,"strokeWidth":586,"style":587,"markerEnd":588},"434.4","472.4",[251,598],{"x1":599,"y1":584,"x2":600,"y2":584,"stroke":69,"strokeWidth":586,"style":587,"markerEnd":588},"583.2","621.2",[151,602,604],{"id":603},"verification","Verification",[15,606,607],{},"Check the size of the inlined block per template (target ≤14KB compressed). Compare screenshots of the first paint (DevTools Performance filmstrip, or WebPageTest) with and without the full stylesheet blocked — they should match for above-the-fold content. Confirm in the Network panel that no stylesheet is render-blocking, and that FCP and LCP improved in the lab.",[240,609,613],{"className":610,"code":611,"language":612,"meta":245,"style":245},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Quick size check of inlined critical CSS in built HTML.\nfor f in dist\u002Findex.html dist\u002Fblog\u002Fexample-post\u002Findex.html; do\n  node -e \"const h=require('fs').readFileSync('$f','utf8');const m=h.match(\u002F\u003Cstyle[^>]*>([\\s\\S]*?)\u003C\\\u002Fstyle>\u002F);console.log('$f', m? require('zlib').gzipSync(m[1]).length+' B gz':'none')\"\ndone\n# trade-off: gzip size approximates what the user downloads; Brotli is smaller\n# still, so this check is slightly conservative.\n","bash",[33,614,615,620,642,665,670,675],{"__ignoreMap":245},[249,616,617],{"class":251,"line":252},[249,618,619],{"class":255},"# Quick size check of inlined critical CSS in built HTML.\n",[249,621,622,624,627,630,633,636,639],{"class":251,"line":259},[249,623,316],{"class":262},[249,625,626],{"class":266}," f ",[249,628,629],{"class":262},"in",[249,631,632],{"class":273}," dist\u002Findex.html",[249,634,635],{"class":273}," dist\u002Fblog\u002Fexample-post\u002Findex.html",[249,637,638],{"class":266},"; ",[249,640,641],{"class":262},"do\n",[249,643,644,648,651,654,657,660,662],{"class":251,"line":280},[249,645,647],{"class":646},"sQw3B","  node",[249,649,650],{"class":286}," -e",[249,652,653],{"class":273}," \"const h=require('fs').readFileSync('",[249,655,656],{"class":266},"$f",[249,658,659],{"class":273},"','utf8');const m=h.match(\u002F\u003Cstyle[^>]*>([\\s\\S]*?)\u003C\\\u002Fstyle>\u002F);console.log('",[249,661,656],{"class":266},[249,663,664],{"class":273},"', m? require('zlib').gzipSync(m[1]).length+' B gz':'none')\"\n",[249,666,667],{"class":251,"line":313},[249,668,669],{"class":262},"done\n",[249,671,672],{"class":251,"line":333},[249,673,674],{"class":255},"# trade-off: gzip size approximates what the user downloads; Brotli is smaller\n",[249,676,677],{"class":251,"line":346},[249,678,679],{"class":255},"# still, so this check is slightly conservative.\n",[151,681,683],{"id":682},"worked-example-a-documentation-site","Worked Example: A Documentation Site",[15,685,686,687,689],{},"A documentation site built with a static generator had one 120KB stylesheet (from a UI framework) blocking every page; mobile FCP p75 was 1.9s. The team added ",[33,688,35],{}," to the build, generating critical CSS for four templates (home, docs article, API reference, search) at 360px and 1300px widths. Output blocks were 6–11KB compressed. Builds took 40 seconds longer. FCP p75 fell to 1.1s, LCP p75 from 2.4s to 1.6s. A visual regression job compares first-paint screenshots for each template with the full CSS blocked, catching designs that change faster than the critical CSS.",[15,691,692],{},[40,693,47,696,47,699,47,702,47,704,47,707,47,709,47,717,47,724,47,731,47,736,47,740,47,745,47,749,47,753,47,757,47,761,47,765,47,769,47,773,47],{"viewBox":694,"width":43,"role":44,"ariaLabel":695,"style":46},"0 0 760 216","Bar chart of compressed critical CSS size for four templates against a 14KB budget.",[49,697],{"className":698,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],[56,700,701],{},"Inlined critical CSS size by template (compressed)",[60,703,695],{},[49,705],{"x":64,"y":64,"width":65,"height":706,"rx":67,"fill":68,"stroke":69,"style":70},"214",[72,708,701],{"x":74,"y":75,"fill":69,"style":76},[251,710],{"x1":711,"y1":712,"x2":711,"y2":713,"stroke":84,"strokeWidth":586,"strokeDashArray":714,"style":716},"668.0","72.0","194.0",[715,715],"4","stroke-opacity:0.9",[72,718,723],{"x":719,"y":720,"fill":69,"style":721,"textAnchor":722},"109.9","92.0","font-size:13px","end","Home",[49,725],{"x":726,"y":525,"width":727,"height":728,"rx":729,"fill":123,"stroke":123,"style":730},"121.9","429.1","19","3","fill-opacity:0.55;stroke-opacity:0.9",[72,732,735],{"x":733,"y":720,"fill":69,"style":734},"557.0","font-size:12px;font-weight:600","11KB",[72,737,739],{"x":719,"y":738,"fill":69,"style":721,"textAnchor":722},"123.0","Docs article",[49,741],{"x":726,"y":742,"width":743,"height":728,"rx":729,"fill":83,"stroke":84,"style":744},"109.0","273.1","fill-opacity:0.7;stroke-opacity:0.9",[72,746,748],{"x":747,"y":738,"fill":69,"style":734},"400.9","7KB",[72,750,752],{"x":719,"y":751,"fill":69,"style":721,"textAnchor":722},"154.0","API reference",[49,754],{"x":726,"y":755,"width":756,"height":728,"rx":729,"fill":123,"stroke":123,"style":730},"140.0","351.1",[72,758,760],{"x":759,"y":751,"fill":69,"style":734},"479.0","9KB",[72,762,764],{"x":719,"y":763,"fill":69,"style":721,"textAnchor":722},"185.0","Search",[49,766],{"x":726,"y":767,"width":768,"height":728,"rx":729,"fill":83,"stroke":84,"style":744},"171.0","234.1",[72,770,772],{"x":771,"y":763,"fill":69,"style":734},"361.9","6KB",[72,774,778],{"x":711,"y":775,"fill":776,"style":777,"textAnchor":527},"68.0","#8a6608","font-size:12px;font-weight:700","budget",[151,780,782],{"id":781},"keeping-critical-css-in-sync","Keeping Critical CSS in Sync",[15,784,785],{},"Generated critical CSS is only as fresh as the last build, which is normally fine. Problems arise when components above the fold are rendered differently at runtime than in the build — A\u002FB tests that swap the hero, logged-in headers, cookie banners and personalised recommendations. For each, either include their styles explicitly (most tools accept a list of selectors to force-include) or render them below the fold initially. Add a CI check that fails the build if the inlined block grows beyond the budget; growth usually means a new component landed above the fold or extraction started including too much.",[151,787,789],{"id":788},"common-mistakes","Common Mistakes",[156,791,792,798,804,810],{},[159,793,794,797],{},[162,795,796],{},"One viewport only."," Mobile-only critical CSS leaves desktop first paint broken, or vice versa.",[159,799,800,803],{},[162,801,802],{},"Extracting per URL on large sites."," Builds become very slow; extract per template.",[159,805,806,809],{},[162,807,808],{},"Removing the critical rules from the full stylesheet."," If the full CSS lacks them, later navigations or JS-rendered content can break.",[159,811,812,815],{},[162,813,814],{},"Ignoring dynamic above-the-fold content."," Banners and personalised blocks flash unstyled.",[151,817,819],{"id":818},"edge-cases","Edge Cases",[15,821,822,825],{},[162,823,824],{},"Server-rendered apps."," Extract at build time for each route template, or use framework features that inline styles used during SSR.",[15,827,828,831,832,835,836,839],{},[162,829,830],{},"Font-face rules."," Include ",[33,833,834],{},"@font-face"," declarations for fonts used above the fold, with ",[33,837,838],{},"font-display",", so text renders with the right fallback strategy.",[15,841,842,845,846,849],{},[162,843,844],{},"CSS custom properties."," Make sure variables defined on ",[33,847,848],{},":root"," that critical rules use are included.",[15,851,852,831,855,858],{},[162,853,854],{},"Dark mode.",[33,856,857],{},"prefers-color-scheme"," rules for above-the-fold elements, or the first paint flashes the wrong theme.",[151,860,862],{"id":861},"faq","FAQ",[864,865,868,872],"details",{"className":866},[867],"faq-item",[869,870,871],"summary",{},"Which viewports should I use?",[15,873,874],{},"At least one small mobile (around 360×740) and one common desktop (around 1300×900). Add a tablet viewport if tablets are a large share of traffic.",[864,876,878,881],{"className":877},[867],[869,879,880],{},"Is Beasties output too large?",[15,882,883],{},"It includes rules used anywhere in the HTML, not only above the fold, so output can be larger. For short pages that is fine; for long pages, a viewport-based tool is more precise.",[864,885,887,890],{"className":886},[867],[869,888,889],{},"Can critical CSS be generated at request time?",[15,891,892],{},"It can, but it is expensive. Cache per template, or generate at build time. Request-time extraction is rarely worth it.",[864,894,896,899],{"className":895},[867],[869,897,898],{},"How do I test that critical CSS is complete?",[15,900,901],{},"Block the full stylesheet in DevTools (Network request blocking) and load the page. The visible area should look correct.",[864,903,905,908],{"className":904},[867],[869,906,907],{},"Does critical CSS help repeat visits?",[15,909,910],{},"Less so — the full stylesheet is usually cached. Some sites skip inlining for returning visitors using a cookie, but this adds complexity and caching complications.",[864,912,914,917],{"className":913},[867],[869,915,916],{},"What about CSS frameworks with utility classes?",[15,918,919],{},"Utility CSS with purging is often small enough that critical extraction is less important; measure first. If the purged file is under about 15KB compressed, inlining it entirely can work.",[864,921,923,926],{"className":922},[867],[869,924,925],{},"Does critical CSS work with a CDN-cached HTML page?",[15,927,928],{},"Yes. Inlined CSS is part of the HTML, so it is cached with it. Regenerate and purge the HTML cache when templates or styles change, just as you would for any markup change.",[151,930,932],{"id":931},"related","Related",[156,934,935,942,949],{},[159,936,937,941],{},[19,938,940],{"href":939},"\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Floading-non-critical-css-asynchronously\u002F","Loading non-critical CSS asynchronously"," — the other half of the pattern.",[159,943,944,948],{},[19,945,947],{"href":946},"\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fremoving-unused-css-with-purgecss\u002F","Removing unused CSS with PurgeCSS"," — shrink the full stylesheet.",[159,950,951,954],{},[19,952,953],{"href":21},"Critical CSS & render-blocking resources"," — the full workflow.",[956,957,959],"script",{"type":958},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Extract Critical CSS at Build Time\",\n  \"description\": \"How to automate critical CSS extraction in a build pipeline so the inlined subset stays accurate, small and in sync with the design.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Choose the tool for your stack\",\n      \"text\": \"For static sites and SSG output, critical gives the smallest output.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Generate per template at multiple viewports\",\n      \"text\": \"Generate per template at multiple viewports\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Apply per-template output to all pages of that template\",\n      \"text\": \"For sites with thousands of pages, generate critical CSS for one representative page per template and inject it into every page of that template during the build.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Load the full stylesheet asynchronously\",\n      \"text\": \"critical with inline: true rewrites stylesheet links to load asynchronously (media swap with a noscript fallback).\"\n    }\n  ]\n}\n",[956,961,962],{"type":958},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Extract Critical CSS at Build Time\",\n  \"description\": \"How to automate critical CSS extraction in a build pipeline so the inlined subset stays accurate, small and in sync with the design.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time\u002F\"\n  }\n}\n",[956,964,965],{"type":958},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Rendering & CSS Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Frendering-css-performance\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Critical CSS & Render-Blocking Resources\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Extracting Critical CSS at Build Time\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time\u002F\"\n    }\n  ]\n}\n",[967,968,969],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}",{"title":245,"searchDepth":259,"depth":259,"links":971},[972,973,974,980,981,982,983,984,985,986],{"id":153,"depth":259,"text":154},{"id":186,"depth":259,"text":187},{"id":214,"depth":259,"text":215,"children":975},[976,977,978,979],{"id":219,"depth":280,"text":220},{"id":237,"depth":280,"text":238},{"id":464,"depth":280,"text":465},{"id":471,"depth":280,"text":472},{"id":603,"depth":259,"text":604},{"id":682,"depth":259,"text":683},{"id":781,"depth":259,"text":782},{"id":788,"depth":259,"text":789},{"id":818,"depth":259,"text":819},{"id":861,"depth":259,"text":862},{"id":931,"depth":259,"text":932},"How to automate critical CSS extraction in a build pipeline so the inlined subset stays accurate, small and in sync with the design.","md",{"slug":990,"type":991,"breadcrumb":992,"datePublished":999,"dateModified":999},"extracting-critical-css-at-build-time","article",[993,995,996,997],{"name":723,"url":994},"\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":998},"\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time\u002F","2026-10-06",true,"\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time",{"title":1003,"description":1004},"Extracting Critical CSS at Build Time per Template","Generate and inline critical CSS automatically in your build with critical or Beasties: choose viewports, handle templates, avoid FOUC, and keep it under 14KB.","rendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Fextracting-critical-css-at-build-time\u002Findex","GSKJfB6Z00S9RbC8xikvHq4DCOhCO3-uMMswKlzZGw8",[1008,1011],{"title":22,"path":1009,"stem":1010,"children":-1},"\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources","rendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Findex",{"title":1012,"path":1013,"stem":1014,"children":-1},"Loading Non-Critical CSS Asynchronously","\u002Frendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Floading-non-critical-css-asynchronously","rendering-css-performance\u002Fcritical-css-and-render-blocking-resources\u002Floading-non-critical-css-asynchronously\u002Findex",1791308077657]