critical README

The project's README from github.com/addyosmani/critical. As one markdown file: README.md.

critical

Extract the critical-path (above-the-fold) CSS from your HTML, inline it, and load the rest asynchronously — so the browser can paint the first screen without waiting on a full stylesheet.

Critical removes render-blocking CSS from the critical path, which is one of the most direct levers on Largest Contentful Paint and first render. It works for static sites, MPAs, and single-page apps, and it's built to be driven by humans, build pipelines, and coding agents alike.

npm install --save-dev critical

Contents


How it works

Given an HTML document and its stylesheets, Critical:

  1. Determines what's critical. It figures out which CSS rules style the content that paints above the fold — either by matching rules against the delivered DOM (fast, no browser) or by rendering the page in a real viewport (accurate, handles SPAs). See Two engines.
  2. Inlines it. The critical rules go into a <style> at the top of <head> so first paint needs only the HTML.
  3. Defers the rest. Each <link rel="stylesheet"> is replaced in the head by a <link rel="preload"> and moved to the end of the body, so it no longer blocks first paint. No inline scripts are added, so it works under a strict Content Security Policy.

The output is deterministic: the same input produces byte-identical output, so it's safe to run in CI and diff in version control.

Quick start

CLI

# Optimize a build directory in place (inlines critical CSS, defers the rest)
critical ./dist --inline --write

# See what it would do and why, without writing anything
critical ./dist --explain

# A single file to stdout
critical index.html --inline > index.critical.html

API

import { critical } from "critical";

const { html, css, report } = await critical({
  src: "dist/index.html",
  inline: true,
});

// `html`   — the document with critical CSS inlined and stylesheets deferred
// `css`    — the critical CSS on its own (minified)
// `report` — structured diagnostics (engine used, bytes, rules, warnings, timing)

critical() never writes to disk on its own — it returns the result. Use the CLI's --write / --out, or write result.html / result.css yourself.

Docker

The container image includes Playwright and Chromium, so both the static and render engines work without additional setup. Mount the generated site at /site and include critical before its arguments:

docker pull ghcr.io/addyosmani/critical:master
docker run --rm -v "$PWD/dist:/site" ghcr.io/addyosmani/critical:master \
  critical . --inline --write

Two engines

Critical picks the right strategy for each document automatically (engine: "auto", the default), and tells you which it used and why.

Engine How it decides what's critical Needs a browser Best for
static Matches CSS rules against the delivered DOM (the used CSS) No SSG / SSR / MPA output that ships real markup
render Loads the page at a real viewport and measures what's painted above the fold Yes (Playwright) SPA shells, and when you want a tight, viewport-accurate set

Automatic routing. When engine is "auto", Critical inspects the delivered HTML. If the document already contains rendered content, the static engine is correct and runs in milliseconds. If the document is an empty application shell (e.g. <div id="root"></div> with no markup yet), there's nothing to match against statically, so Critical escalates to the render engine and measures the page the way a browser actually paints it.

You can always pin the engine explicitly with engine: "static" or engine: "render".

The render engine uses Playwright, declared as an optional peer dependency. It's imported lazily, so the static path — and the default install — never pull in a browser. To use the render engine: npm i -D playwright && npx playwright install chromium.

CLI reference

critical <input> [options]

  input        a directory (all *.html are processed), an .html file, or stdin
Option Description Default
-e, --engine <auto|static|render> Engine selection auto
-i, --inline Inline critical CSS and defer the rest off
-w, --width <px> Render-engine viewport width 1300
-h, --height <px> Render-engine viewport height 900
--dimensions <WxH,WxH> Multiple render viewports (e.g. 390x844,1300x900) —
--no-fold Ignore [data-critical-fold] scoping in the static engine —
--no-minify Keep the critical CSS readable instead of minifying —
-o, --out <file|dir> Write output here instead of stdout stdout
--write Rewrite the input file(s) in place (use with --inline) off
--json Emit the structured result as JSON off
--explain Print the engine decision and stats to stderr off
--help Show help —

Examples

critical ./dist --inline --write              # whole build dir, in place
critical index.html --explain                 # routing decision + size stats
critical app.html -e render -i > out.html     # force a real-browser pass for an SPA
critical page.html --dimensions 390x844,1300x900 -i   # union of mobile + desktop folds
cat page.html | critical --inline             # stdin -> stdout
critical ./dist --json                        # machine-readable report for CI/agents

API reference

import { critical } from "critical";

critical(options) → Promise<{ html, css, report }>

Option Type Default Description
src string — Path or URL to an HTML file. Provide src or html.
html string — Raw HTML source. Takes precedence over src.
css string | string[] — Extra CSS: file paths, globs, or raw CSS strings, beyond what the document links.
base string dir of src, else cwd Base directory for resolving stylesheet/asset paths.
engine "auto" | "static" | "render" "auto" Engine selection (see Two engines).
inline boolean | object false Inline critical CSS and defer the rest. Pass an object to configure inlining.
minify boolean true Minify the critical CSS (via Lightning CSS).
foldAware boolean true Honor [data-critical-fold] scoping in the static engine.
width number 1300 Render-engine viewport width.
height number 900 Render-engine viewport height.
dimensions Array<{width, height}> — Multiple render viewports; their critical sets are unioned. Overrides width/height.
timeout number 30000 Render-engine navigation timeout (ms).
userAgent string — User agent for the render engine.

The inline object accepts:

Key Type Default Description
preload boolean true Insert a <link rel="preload" as="style"> hint where each deferred sheet was.
nonce string — Nonce to set on the injected <style>, for a strict style-src CSP.

The result

const { html, css, report } = await critical({ src: "dist/index.html", inline: true });
{
  "engine": "static", // engine that actually ran
  "reason": "rendered document (…) — matching used CSS without a browser",
  "requestedEngine": "auto",
  "rules": { "kept": 10, "total": 15 },
  "bytes": { "stylesheets": 1107, "critical": 544, "savedBlocking": 1107 },
  "stylesheetsDiscovered": ["/styles.css"],
  "stylesheetsDeferred": ["/styles.css"], // present when inlined
  "warnings": ["…"],
  "durationMs": 13,
  "deterministic": true,
}

report is designed to be read by a program: it explains the decision, quantifies the win (savedBlocking = render-blocking bytes removed from the critical path), and surfaces any caveats as warnings.

Inlining behavior

With inline: true, Critical:

  1. Inserts <style data-critical>…</style> as the first child of <head> so it wins the cascade race against the deferred sheets.

  2. Stops each <link rel="stylesheet"> from blocking the first paint, using the preload strategy:

    <!-- in the head, where the stylesheet was: -->
    <link rel="preload" as="style" href="/styles.css" />
    
    <!-- moved to the end of <body>: -->
    <link rel="stylesheet" href="/styles.css" />
    

    The preload starts the download early; the stylesheet applies after the above-the-fold content (already styled by the inlined critical CSS) has painted. There is no inline event handler, so this works under a strict script-src Content Security Policy, and no <noscript> fallback is needed — the stylesheet loads normally whether or not JavaScript runs.

Add data-critical-skip to a <link> to leave it untouched. For a strict style-src CSP, pass inline: { nonce: "…" } to stamp the injected <style> with your nonce.

Tuning the above-the-fold set

MCP server (agents)

Critical ships a Model Context Protocol server so an agent can call it as a tool and receive the structured report back directly:

node src/mcp.js     # stdio MCP server exposing `optimize_critical_css`
import { createServer } from "critical/mcp";

The tool takes src/html (plus optional css, engine, inline, width, height) and returns the critical CSS, the rewritten HTML, and the report as structured content. Requires the optional @modelcontextprotocol/sdk peer dependency.

Recipes

Optimize a static build directory (in CI):

critical ./dist --inline --write

Programmatically, writing both outputs:

import { writeFile } from "node:fs/promises";
import { critical } from "critical";

const { html, css } = await critical({ src: "dist/index.html", inline: true });
await writeFile("dist/index.html", html);
await writeFile("dist/critical.css", css);

An SPA where the shell is empty until JS runs:

await critical({ src: "dist/index.html", engine: "render", inline: true });
// auto-routing would also pick "render" here; pinning it just skips the sniff.

Design principles

Requirements

Alternatives

Critical isn't the only way to inline critical CSS, and it isn't always the right one — a purely prerendered site that just wants its used CSS inlined may be better served by a lighter, browser-free tool. For a thorough, fair comparison of Critical, Beasties (formerly Critters), Penthouse, and native platform approaches — including where each one is the better choice and a reproducible benchmark — see COMPARISON.md.

Maintainers

This module is brought to you and maintained by the following people:

License

Apache-2.0 © Addy Osmani

critical (addyosmani/critical)

Searching Database…

10.3K ·GitHub
这是干什么的?它从你的 HTML 页面中提取首屏(关键路径)CSS,内联到文档里,其余部分异步加载,这样浏览器不用等完整样式表就能渲染出第一屏。
它是命令行工具、库,还是应用?三种形态都有:终端里用的 CLI、供构建脚本调用的 Node API,还有一个 MCP 服务器,让编程 agent 可以把它当作工具调用并拿到结构化的返回结果。
怎么把它跑起来?从 npm 安装——需要 Node.js 22.13 或更高版本——然后用 CLI 处理你的 HTML 文件,或者在 Node 脚本里调用它的 API。
必须装无头浏览器才能用吗?只有 render 引擎需要,它用的是 playwright 加 Chromium(npm i -D playwright && npx playwright install chromium)。static 引擎两个都不需要。
怎么控制哪些内容算首屏?给首屏容器加上 data-critical-fold 属性来限定 static 引擎的作用范围,或者为 render 引擎设置 width/height 尺寸;多个尺寸会取并集,所以一份 critical CSS 可以同时覆盖手机和桌面。
能在 CI 里处理整个构建输出目录吗?可以——README 里就有在 CI 中优化静态构建目录的示例,你也可以在 Node 脚本里以编程方式同时写出 critical CSS 和改写后的 HTML。
它和 Penthouse 或 Beasties 比怎么样?仓库里带了一份 COMPARISON.md,用可复现的基准测试对比了 Critical、Beasties(原名 Critters)、Penthouse 以及浏览器原生方案;其中提到纯预渲染的网站用更轻、无需浏览器的工具可能更合适。
Creator
addyosmani
Size
4.7 MB
Stars
10.3K
Age
12y
Commits · 90 Days