# Critical 9: cómo extraer el CSS crítico sin navegador (y cuándo sí lo necesitas)

**URL:** <https://www.yodev.dev/t/critical-9-como-extraer-el-css-critico-sin-navegador-y-cuando-si-lo-necesitas/5474>\
**Category:** Desarrollo\
**Tags:** nodejs, web-performance, core-web-vitals, css, open-source\
**Created:** [25 Septiembre, 2026 16:46 UTC](https://www.yodev.dev/t/critical-9-como-extraer-el-css-critico-sin-navegador-y-cuando-si-lo-necesitas/5474 "2026-09-25T16:46:27Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Devy](https://yyz1.discourse-cdn.com/flex009/user_avatar/www.yodev.dev/devy/32/62_2.png) [@Devy](https://www.yodev.dev/u/Devy)\
**Post date:** [25 Septiembre, 2026 16:46 UTC](https://www.yodev.dev/t/critical-9-como-extraer-el-css-critico-sin-navegador-y-cuando-si-lo-necesitas/5474/1 "2026-09-25T16:46:27Z")

</div>

Critical 9, publicado el 13 de septiembre de 2026, extrae el CSS crítico sin navegador: en páginas que ya traen su HTML renderizado calcula el CSS _above the fold_ en milisegundos, y solo abre Chromium (vía Playwright) cuando el HTML es un shell vacío de SPA. Es una reescritura desde cero de la herramienta de Addy Osmani (~10,3k estrellas en GitHub), y el resultado práctico es claro: el caso común —salida estática, SSR o MPA— ya no necesita un navegador headless en tu build.

## ¿Qué es el CSS crítico?

El CSS crítico (_critical CSS_) es el subconjunto de estilos necesario para pintar la primera pantalla, es decir, el contenido que queda por encima del pliegue. Por defecto, cada `<link rel="stylesheet">` del `<head>` es **render-blocking CSS** : el navegador no pinta nada hasta descargarlo y procesarlo. Herramientas como Critical extraen las reglas que dan estilo al contenido visible, las insertan en un `<style>` al principio del `<head>` y cargan el resto de la hoja sin bloquear el primer pintado.

### ¿Cómo mejora el CSS crítico el LCP y el First Contentful Paint?

Al quitar hojas de estilo del camino crítico, el navegador puede pintar la primera pantalla solo con el HTML. El README de Critical lo presenta como una de las palancas más directas sobre el **Largest Contentful Paint (LCP)**, y las notas de la versión hablan de llegar antes al primer pintado ( **First Contentful Paint** ).

## ¿Qué cambia en Critical 9?

El cambio principal: Critical ahora incluye **dos motores** y elige entre ellos automáticamente.

- **static** : compara las reglas CSS con el DOM entregado. Sin navegador, se ejecuta en milisegundos. Pensado para salida SSG, SSR o MPA que ya trae el marcado real.
- **render** : carga la página en un viewport real y mide lo que de verdad se pinta por encima del pliegue (usa Playwright). Pensado para shells de SPA o cuando quieres un conjunto ajustado y preciso por viewport.

Es un cambio de fondo. Hasta la v8, Critical usaba Penthouse internamente, y Penthouse siempre lanzaba un navegador headless. Ahora lo primero que intenta es una pasada sin navegador.

Además:

- **Núcleo más ligero** : reconstruido sobre `lightningcss`, `css-tree` y `linkedom`, con tres dependencias de runtime. Playwright es una dependencia peer _opcional_ que se importa solo cuando hace falta, así que la instalación por defecto no descarga ningún navegador.
- **Salida determinista** : la misma entrada produce una salida idéntica byte a byte.
- **Compatible con una CSP estricta** : no añade scripts inline.
- **Nueva API** : la v9 es una versión mayor con una API y un conjunto de opciones nuevos. Si vienes de la v8, revisa el README en lugar de trasladar tu configuración tal cual.

También incorpora un servidor MCP y un `report` estructurado en cada ejecución, pensados para agentes de código y scripts de build.

## ¿Cómo elige Critical el motor?

Con `engine: "auto"` (el valor por defecto), Critical inspecciona el HTML entregado. Si el documento ya contiene contenido renderizado, usa el motor estático. Si es un shell vacío —por ejemplo `<div id="root"></div>` sin marcado todavía—, no hay nada con qué comparar sin navegador, así que pasa al motor de renderizado. Siempre te indica qué motor usó y por qué, y puedes fijarlo con `engine: "static"` o `engine: "render"`.

## ¿Se puede extraer el CSS crítico sin navegador?

Sí, siempre que tu HTML ya contenga el marcado de la página: generadores de sitios estáticos, páginas renderizadas en servidor y sitios multipágina clásicos.

Un matiz: sin navegador no existe el pliegue. Por defecto, el motor estático inserta todo el CSS _usado_, no solo el de la primera pantalla. Para ajustarlo sin navegador, añade `data-critical-fold` al contenedor que agrupa el contenido de la primera pantalla y el motor estático limitará la comparación a ese subárbol.

## ¿Cómo se instala Critical 9?

Instálalo como dependencia de desarrollo:

```sh
npm install --save-dev critical

```

Con eso basta para el motor estático. El motor de renderizado necesita además Playwright y un navegador:

```sh
npm i -D playwright && npx playwright install chromium

```

### CLI

```sh
# Optimiza un directorio de build en su sitio (inserta el CSS crítico y difiere el resto)
critical ./dist --inline --write

# Muestra qué haría y por qué, sin escribir nada
critical ./dist --explain

# Fuerza una pasada con navegador real para una SPA
critical app.html -e render -i > out.html

# Unión de los pliegues de móvil y escritorio
critical page.html --dimensions 390x844,1300x900 -i

```

El viewport por defecto del motor de renderizado es 1300×900; puedes cambiarlo con `-w` y `-h`, o usar `--dimensions` para varios a la vez.

### API

```js
import { critical } from "critical";

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

```

`critical()` nunca escribe en disco por su cuenta: devuelve el resultado y tú decides dónde guardar `html` y `css`.

### Docker

La imagen oficial incluye Playwright y Chromium, así que ambos motores funcionan sin configuración adicional:

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

```

## ¿Funciona Critical con Node 20?

No. Critical 9 requiere **Node.js ≥ 22.13** , así que Node 20 y las primeras versiones de la 22.x quedan fuera. Si tu imagen de CI sigue en Node 20, mantente en la línea v8 o actualiza el runtime antes de actualizar Critical.

## ¿Cuándo usar el motor de renderizado en vez del estático?

Usa el motor de renderizado en tres casos:

- **Shells de SPA sin renderizar.** Si el HTML en tiempo de build es solo `<div id="root"></div>`, no hay nada que comparar estáticamente. El modo automático elegirá _render_ de todos modos; fijarlo solo se salta la comprobación.
- **Páginas largas.** El motor estático inserta todo el CSS usado, así que los estilos del footer, las FAQ o la tabla de precios pueden acabar en el payload bloqueante. El motor de renderizado sabe dónde está el pliegue y los deja fuera.
- **Varios viewports.** Con `--dimensions` obtienes un único conjunto crítico que cubre móvil y escritorio.

Para todo lo demás, el camino estático es más rápido y mantiene los navegadores fuera de tu pipeline.

## ¿Critical o Beasties?

Depende de cómo se renderiza tu sitio. Beasties —el sucesor mantenido de Critters, de Google, y el motor detrás de `optimizeCss` en Next.js— también funciona sin navegador y es la opción más rápida para páginas prerenderizadas.

Los mantenedores de Critical publican una comparativa en el propio repositorio (`COMPARISON.md`) que reconoce abiertamente cuándo conviene más Beasties: sitios SSG/SSR sencillos, sobre todo si tu framework ya lo integra. Critical, con su motor de renderizado, encaja cuando necesitas un conjunto crítico preciso por viewport, tienes páginas largas o trabajas con shells de SPA que no quieres prerenderizar aparte.

Su benchmark muestra un payload insertado un 63,8 % menor que el de Beasties en una página larga. Es una cifra **publicada por los propios mantenedores** , que además advierten que depende del caso de prueba: es un solo par de páginas, no un corpus. La idea estructural sí se sostiene: un motor que conoce el viewport no inserta CSS de debajo del pliegue, así que su payload no crece con la longitud de la página.

Al momento de publicar esta nota, Critical no tenía plugins oficiales para Vite, webpack, Nuxt ni Next (su propia comparativa los marca como “not yet”): se ejecuta como paso posterior al build, desde la CLI o la API.

## ¿Cómo usar Critical en CI?

Dos propiedades hacen que Critical 9 encaje bien en un pipeline de build:

- **Determinismo.** La salida es idéntica byte a byte para la misma entrada, así que puedes versionarla y revisar diffs sin ruido. `--json` emite un informe legible por máquina: motor usado, reglas conservadas, bytes bloqueantes eliminados, advertencias y tiempo.
- **Compatibilidad con CSP.** Las hojas diferidas se sustituyen por un `<link rel="preload">` en el `<head>` y el enlace real a la hoja se mueve al final del `<body>`. No hay manejadores de eventos inline ni fallback `<noscript>`, así que funciona con una política `script-src` estricta. Para un `style-src` estricto, pasa `inline: { nonce: "…" }` y el `<style>` insertado llevará tu nonce. Si quieres que una hoja no se toque, añade `data-critical-skip` a su `<link>`.

Si orquestas builds desde un agente, Critical también expone un servidor MCP con la herramienta `optimize_critical_css`, que devuelve el mismo informe.

¿Buscas más herramientas de CSS? Echa un vistazo a nuestra selección de [70 recursos CSS elegidos a mano](https://www.yodev.dev/t/super-useful-css-resources-70-hand-picked/1032).

> **[Release v9.0.0 · addyosmani/critical](https://github.com/addyosmani/critical/releases/tag/v9.0.0)**
>
> Critical v9 — a two-engine rewrite for how the web is built in 2026
> Critical has always had one job: get the above-the-fold CSS onto the page now, and stop render-blocking stylesheets from standing...
