Ejecutar librerías de React en Preact con preact/compat
Usa preact/compat para ejecutar librerías React en Preact, con alias para Vite, webpack, Rollup, Jest, TypeScript y fallos comunes.
preact/compat es una capa de compatibilidad que se distribuye dentro del paquete principal preact desde Preact X, y que mapea la API pública de React sobre Preact para que la mayoría de las librerías de React funcionen sin modificaciones, mientras tu aplicación envía alrededor de 9,5 KB de framework en lugar del runtime mucho más grande de React.
Cambiar el alias es un trabajo de cinco minutos. Descubrir tres días después que un date picker está lanzando errores desde algún punto profundo de node_modules es la parte de la que nadie te advierte. Se habilita creando alias de react y react-dom hacia preact/compat en tu bundler: sin cambios de código en tus componentes, sin paquetes adicionales por instalar. Esta guía te da la configuración exacta de alias para cada toolchain importante, un recorrido de migración con el beneficio en tamaño de bundle y un relato honesto de qué librerías se rompen.
Puntos clave
preact/compatse distribuye dentro del paquetepreact. Compat ahora vive en el core, por lo que el paquete independientepreact-compates obsoleto ynpm install preactes todo lo que necesitas.- Todo el mecanismo consiste en crear alias de cuatro rutas de importación:
react,react-dom,react-dom/test-utilsyreact/jsx-runtime, todas apuntando a Preact. - Con
@preact/preset-vite, el aliasing es automático, así que no escribesresolve.aliasa mano. - En webpack, el alias de
react-domdebe ir por debajo dereact-dom/test-utils, o la regla más amplia ocultará el mapeo de test-utils. - Compat cubre la API pública de React, no sus internos. Las librerías que acceden a rutas internas profundas de
react-dom, o que dependen de las APIs más nuevas de React 19, todavía pueden romperse.
¿Qué es preact/compat y por qué existe?
preact/compat traduce la superficie de la API pública de React (React.Component, hooks, createPortal, forwardRef, memo, el runtime de JSX) a sus equivalentes en Preact, de modo que los componentes de terceros escritos para React se resuelvan a Preact en tiempo de compilación. El propio listado en npm de Preact promociona la librería por su amplio soporte de React detrás de un único alias, y esa compatibilidad es lo que te permite reutilizar el ecosistema de React sin reescribir nada.
Ya no hay ningún paquete preact-compat que instalar. La guía oficial de actualización explica que la capa se distribuía por separado y se integró en el repositorio principal para simplificar la coordinación, así que quien actualice debe reemplazar los antiguos imports y alias de preact-compat por preact/compat. El paquete sin ámbito (unscoped) es un callejón sin salida: su repositorio de GitHub está archivado y en modo solo lectura desde diciembre de 2021, y su página de npm te indica que lo desinstales, ya que Preact X incluye compat por defecto. Preact 10.x es la línea estable actual, con la 11.0.0 en fase de release candidate y no de disponibilidad general; la página de releases de Preact enumera los números de versión exactos.
El aliasing es todo el truco
Discover how at OpenReplay.com.
Todo el mecanismo es el aliasing: apuntas react, react-dom, react-dom/test-utils y react/jsx-runtime hacia Preact para que cada importación existente, incluidas las de librerías de terceros en las profundidades de node_modules, se resuelva a preact/compat en lugar de React. Nada en el código de tus componentes cambia. Una sentencia import { useState } from 'react' permanece exactamente como está escrita; el bundler reescribe dónde se resuelve react.
Las cuatro entradas canónicas, según la guía de Preact sobre cómo crear alias de React a Preact:
| Ruta de importación | Destino del alias | Motivo |
|---|---|---|
react | preact/compat | API principal de React |
react-dom/test-utils | preact/test-utils | Utilidades de test |
react-dom | preact/compat | Renderizador del DOM (debe situarse por debajo de test-utils) |
react/jsx-runtime | preact/jsx-runtime | Transformación automática de JSX |
Crear alias solo de react y react-dom, un atajo habitual en tutoriales antiguos, deja que las librerías que importan el runtime de JSX o react-dom/test-utils se resuelvan a React, lo cual reintroduce los bytes que intentabas eliminar.
Configuración de alias por toolchain
Vite (opción recomendada por defecto)
Con @preact/preset-vite, el aliasing es automático y no deberías escribir resolve.alias a mano. El preset activa los alias de React por ti: la opción reactAliasesEnabled los controla y está en true a menos que la desactives. También configura la transformación de JSX por ti.
// vite.config.ts
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';
export default defineConfig({
plugins: [preact()], // JSX + react→preact/compat aliasing handled automatically
});
Si ejecutas Vite sin el preset, añade la alternativa manual:
export default defineConfig({
resolve: {
alias: {
react: 'preact/compat',
'react-dom/test-utils': 'preact/test-utils',
'react-dom': 'preact/compat',
'react/jsx-runtime': 'preact/jsx-runtime',
},
},
});
Webpack
En webpack, el alias de react-dom debe ir por debajo de react-dom/test-utils; de lo contrario, la regla más amplia de react-dom oculta el mapeo de test-utils y las utilidades de test se resuelven silenciosamente al módulo incorrecto.
const config = {
resolve: {
alias: {
react: 'preact/compat',
'react-dom/test-utils': 'preact/test-utils',
'react-dom': 'preact/compat', // Must be below test-utils
'react/jsx-runtime': 'preact/jsx-runtime',
},
},
};
Rollup
Instala @rollup/plugin-alias y regístralo antes de @rollup/plugin-node-resolve, para que las reescrituras ocurran antes de que Rollup resuelva los módulos.
import alias from '@rollup/plugin-alias';
export default {
plugins: [
alias({
entries: [
{ find: 'react', replacement: 'preact/compat' },
{ find: 'react-dom/test-utils', replacement: 'preact/test-utils' },
{ find: 'react-dom', replacement: 'preact/compat' },
{ find: 'react/jsx-runtime', replacement: 'preact/jsx-runtime' },
],
}),
],
};
Node / Next.js (sin alias de bundler)
Los runtimes de Node ignoran los alias del bundler, Next.js incluido, así que el alias va en package.json en su lugar, utilizando el paquete publicado @preact/compat. Ese paquete con ámbito (scoped) existe únicamente para que el aliasing nativo de npm tenga algo a lo que apuntar; su única función es reexportar preact/compat sin cambios. Ten en cuenta que el @preact/compat con ámbito no es el difunto preact-compat sin ámbito.
{
"dependencies": {
"react": "npm:@preact/compat",
"react-dom": "npm:@preact/compat"
}
}
Jest
Jest reescribe las rutas de módulos con entradas de regex bajo moduleNameMapper:
{
"moduleNameMapper": {
"^react$": "preact/compat",
"^react-dom/test-utils$": "preact/test-utils",
"^react-dom$": "preact/compat",
"^react/jsx-runtime$": "preact/jsx-runtime"
}
}
TypeScript
TypeScript resuelve los tipos de forma independiente a tu bundler, así que mapea las rutas en tsconfig.json y habilita skipLibCheck. Activa skipLibCheck porque un puñado de librerías de React se apoyan en tipos que compat no incluye, y un análisis completo de cada .d.ts en node_modules fallará en esas declaraciones.
{
"compilerOptions": {
"skipLibCheck": true,
"baseUrl": "./",
"paths": {
"react": ["./node_modules/preact/compat/"],
"react/jsx-runtime": ["./node_modules/preact/jsx-runtime"],
"react-dom": ["./node_modules/preact/compat/"],
"react-dom/*": ["./node_modules/preact/compat/*"]
}
}
}
Un recorrido mínimo de migración
Migrar una aplicación React existente a Preact con herramientas modernas es una operación de cuatro pasos:
- Cambia las dependencias. Elimina
react,react-domy sus@types, ya que Preact incluye sus propios tipos de TypeScript; luego ejecutanpm install preactynpm install -D @preact/preset-vite. - Añade el preset. Pon
preact()en los plugins de Vite. Se encarga tanto de la transformación de JSX como del aliasreact → preact/compat, así que puedes eliminar cualquier configuración manual deesbuild.jsxInject/jsxFactoryde instalaciones antiguas de la era de Vite 2. - Cambia el punto de entrada del render. Sustituye la llamada de montaje de React DOM por el
renderde Preact:
// Before
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// After
import { render } from 'preact';
render(<App />, document.getElementById('root'));
- Compila y comprueba el tamaño. El beneficio es lo que importa: el core de Preact más
preact/compatse sitúa en aproximadamente 9,5 KB min+gzip, frente a algo más cercano a 60 KB de React 19 más React DOM, casi todo ello en el punto de entradareact-dom/client. Para una aplicación cuyos componentes ya se resuelven a través de compat, esa diferencia es prácticamente gratis.
¿Cuándo se rompe preact/compat?
Compat cubre la API pública de React, no sus internos. Las librerías que acceden a rutas internas privadas de react-dom, o que se apoyan en algunas de las APIs más recientes de React 19, pueden romperse incluso cuando el alias es correcto, así que verifica cada dependencia antes de desplegar. Las discrepancias de tipos provenientes de librerías tipadas para React son esperadas y se gestionan con skipLibCheck; los fallos en tiempo de ejecución son los que hay que vigilar, y las repeticiones de sesión de estas integraciones a menudo los revelan como errores de consola lanzados desde dentro de una dependencia y no desde tu propio código.
Una comprobación rápida antes de adoptar una librería:
- Haz un grep del paquete buscando importaciones internas profundas de
react-dom/, la señal de ruptura más común. - Comprueba si hay APIs exclusivas de React 19 de las que dependa la librería; verifica la cobertura contra la release actual de Preact en lugar de darlo por sentado.
- Ejecuta la propia suite de tests de la librería con el
moduleNameMapperde Jest anterior para detectar fallos pronto. - Haz una prueba de humo en desarrollo, vigilando la consola en busca de errores originados dentro de la dependencia.
Los usuarios de SSR y de frameworks se enfrentan a una clase distinta de problema: como los alias del bundler no se aplican en Node, Next.js y runtimes similares necesitan el alias en package.json, y la ruta ssrLoadModule de Vite puede saltarse parte de la configuración de alias, así que confirma que tanto el cliente como el servidor se resuelven a preact/compat.
Crea los alias de las cuatro entradas para tu toolchain, ejecuta los tests de tus dependencias a través del mismo mapeo y podrás reutilizar la mayor parte del ecosistema de React con una fracción de los bytes. Las excepciones honestas son las librerías acopladas a los internos de React en lugar de a su API pública. Empieza añadiendo @preact/preset-vite en una rama y midiendo tu bundle de producción antes y después.
Preguntas frecuentes
¿Cuál es la diferencia entre @preact/compat y el antiguo paquete preact-compat?
El paquete con ámbito @preact/compat es un paquete npm activo que reexporta preact/compat, y se usa únicamente para crear alias de react a través de package.json en runtimes de Node como Next.js, donde los alias del bundler no se aplican. El preact-compat sin ámbito es un paquete diferente y archivado cuyo repositorio está en modo solo lectura desde diciembre de 2021; estaba orientado a Preact 8.x, y Preact X ahora incluye compat dentro del core. Nunca instales el que no tiene ámbito.
¿Sigo necesitando escribir resolve.alias manualmente si uso @preact/preset-vite?
No. Con @preact/preset-vite, el aliasing de react y react-dom hacia preact/compat se hace automáticamente, controlado por la opción reactAliasesEnabled, que está activada a menos que la desactives. Añadir preact() a tus plugins de Vite se encarga tanto de la transformación de JSX como del aliasing, por lo que escribir resolve.alias a mano es redundante y puede generar conflictos. Solo escribes el bloque manual de cuatro entradas cuando ejecutas Vite sin el preset.
¿Por qué mis utilidades de test se resuelven al módulo incorrecto después de crear los alias en webpack?
Porque el alias de react-dom está listado por encima de react-dom/test-utils en tu configuración de webpack. Webpack hace coincidir primero la regla más amplia de react-dom, así que oculta el mapeo más específico de test-utils y las utilidades de test se resuelven silenciosamente a preact/compat en lugar de preact/test-utils. Corrígelo colocando la entrada de react-dom por debajo de react-dom/test-utils. Rollup tiene una regla de orden relacionada: coloca @rollup/plugin-alias antes de @rollup/plugin-node-resolve.
¿Por qué se rompe una librería de React aunque mi alias de preact/compat sea correcto?
Porque compat mapea la API pública de React, no sus internos. Las librerías que importan rutas internas profundas de react-dom, o que dependen de las APIs más recientes de React 19, pueden fallar en tiempo de ejecución incluso con un alias correcto. Estos fallos aparecen como errores de consola lanzados desde dentro de la dependencia, no desde tu propio código. Antes de adoptar una librería, hazle un grep buscando importaciones profundas de react-dom/ y ejecuta su propia suite de tests con el moduleNameMapper de Jest.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k