12k
All articles

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.

OpenReplay Team
OpenReplay Team
Ejecutar librerías de React en Preact con preact/compat

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/compat se distribuye dentro del paquete preact. Compat ahora vive en el core, por lo que el paquete independiente preact-compat es obsoleto y npm install preact es todo lo que necesitas.
  • Todo el mecanismo consiste en crear alias de cuatro rutas de importación: react, react-dom, react-dom/test-utils y react/jsx-runtime, todas apuntando a Preact.
  • Con @preact/preset-vite, el aliasing es automático, así que no escribes resolve.alias a mano.
  • En webpack, el alias de react-dom debe ir por debajo de react-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

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ónDestino del aliasMotivo
reactpreact/compatAPI principal de React
react-dom/test-utilspreact/test-utilsUtilidades de test
react-dompreact/compatRenderizador del DOM (debe situarse por debajo de test-utils)
react/jsx-runtimepreact/jsx-runtimeTransformació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:

  1. Cambia las dependencias. Elimina react, react-dom y sus @types, ya que Preact incluye sus propios tipos de TypeScript; luego ejecuta npm install preact y npm install -D @preact/preset-vite.
  2. Añade el preset. Pon preact() en los plugins de Vite. Se encarga tanto de la transformación de JSX como del alias react → preact/compat, así que puedes eliminar cualquier configuración manual de esbuild.jsxInject / jsxFactory de instalaciones antiguas de la era de Vite 2.
  3. Cambia el punto de entrada del render. Sustituye la llamada de montaje de React DOM por el render de Preact:
// Before
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));

// After
import { render } from 'preact';
render(<App />, document.getElementById('root'));
  1. Compila y comprueba el tamaño. El beneficio es lo que importa: el core de Preact más preact/compat se 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 entrada react-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 moduleNameMapper de 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.

DevTools for the frontend

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

We use cookies to improve your experience. By using our site, you accept cookies.