12k
All articles

React-Bibliotheken mit preact/compat in Preact betreiben

Nutzen Sie preact/compat, um React-Libraries in Preact auszuführen, mit Alias-Konfigurationen für Vite, webpack, Rollup, Jest, TypeScript und typische Fehler.

OpenReplay Team
OpenReplay Team
React-Bibliotheken mit preact/compat in Preact betreiben

preact/compat ist eine Kompatibilitätsschicht, die seit Preact X im Hauptpaket preact enthalten ist. Sie bildet die öffentliche API von React auf Preact ab, sodass die meisten React-Bibliotheken unverändert laufen – während deine App statt der deutlich umfangreicheren React-Runtime nur rund 9,5 KB Framework ausliefert.

Das Alias einzurichten ist eine Sache von fünf Minuten. Dass drei Tage später ein Datepicker Fehler aus den Tiefen von node_modules wirft, ist der Teil, vor dem dich niemand warnt. Aktiviert wird die Schicht, indem du react und react-dom in deinem Bundler auf preact/compat aliasierst: keine Codeänderungen an deinen Komponenten, kein separates Paket zu installieren. Dieser Leitfaden liefert dir die exakte Alias-Konfiguration für jede gängige Toolchain, eine Migrations-Anleitung samt Bundle-Size-Gewinn und eine ehrliche Einschätzung dazu, welche Bibliotheken Probleme machen.

Die wichtigsten Erkenntnisse

  • preact/compat ist Teil des Pakets preact. Compat lebt inzwischen im Core, das eigenständige Paket preact-compat ist damit obsolet und npm install preact genügt vollständig.
  • Der gesamte Mechanismus besteht darin, vier Importpfade zu aliasieren: react, react-dom, react-dom/test-utils und react/jsx-runtime zeigen allesamt auf Preact.
  • Mit @preact/preset-vite erfolgt das Aliasing automatisch – du schreibst resolve.alias also nicht von Hand.
  • In webpack muss der Alias react-dom unter react-dom/test-utils stehen, sonst überdeckt die allgemeinere Regel das test-utils-Mapping.
  • Compat deckt die öffentliche API von React ab, nicht deren Interna. Bibliotheken, die auf tiefe interne react-dom-Pfade zugreifen oder auf die neuesten React-19-APIs setzen, können weiterhin brechen.

Was ist preact/compat und warum gibt es das?

preact/compat übersetzt die öffentliche API-Oberfläche von React (React.Component, Hooks, createPortal, forwardRef, memo, die JSX-Runtime) in Preact-Äquivalente, sodass Drittanbieter-Komponenten, die gegen React geschrieben wurden, zur Build-Zeit auf Preact aufgelöst werden. Preacts eigener npm-Eintrag bewirbt die Bibliothek mit breiter React-Unterstützung hinter einem einzigen Alias – und genau diese Kompatibilität erlaubt es dir, das React-Ökosystem ohne Neuschreiben weiterzuverwenden.

Ein Paket preact-compat musst du nicht mehr installieren. Der offizielle Upgrade-Guide erklärt, dass die Schicht früher eigenständig ausgeliefert und dann ins Core-Repository überführt wurde, um die Koordination zu vereinfachen. Wer migriert, muss alte preact-compat-Importe und -Aliase daher durch preact/compat ersetzen. Das ungescopte Paket ist eine Sackgasse: Sein GitHub-Repository ist seit Dezember 2021 archiviert und schreibgeschützt, und die npm-Seite fordert zur Deinstallation auf, da Preact X compat standardmäßig mitbringt. Preact 10.x ist die aktuelle Stable-Linie; 11.0.0 befindet sich im Release-Candidate-Stadium und ist noch nicht allgemein verfügbar. Die genauen Versionsnummern findest du auf der Preact-Releases-Seite.

Aliasing ist der ganze Trick

Der gesamte Mechanismus ist Aliasing: Du lässt react, react-dom, react-dom/test-utils und react/jsx-runtime auf Preact zeigen, damit jeder bestehende Import – einschließlich Drittanbieter-Bibliotheken tief in node_modules – auf preact/compat statt auf React aufgelöst wird. An deinem Komponentencode ändert sich nichts. Eine Anweisung wie import { useState } from 'react' bleibt exakt so stehen; der Bundler schreibt lediglich um, wohin react aufgelöst wird.

Die vier kanonischen Einträge, aus Preacts Guide zum Aliasing von React auf Preact:

ImportpfadAlias-ZielWarum
reactpreact/compatKern-API von React
react-dom/test-utilspreact/test-utilsTest-Utilities
react-dompreact/compatDOM-Renderer (muss unter test-utils stehen)
react/jsx-runtimepreact/jsx-runtimeAutomatische JSX-Transformation

Nur react und react-dom zu aliasieren – eine gängige Abkürzung aus älteren Tutorials – führt dazu, dass Bibliotheken, die die JSX-Runtime oder react-dom/test-utils importieren, weiterhin auf React aufgelöst werden. Damit holst du dir genau die Bytes zurück, die du eigentlich loswerden wolltest.

Alias-Konfiguration je Toolchain

Vite (empfohlener Standard)

Mit @preact/preset-vite erfolgt das Aliasing automatisch, und du solltest resolve.alias nicht von Hand schreiben. Das Preset aktiviert die React-Aliase für dich: Die Option reactAliasesEnabled steuert sie und steht auf true, sofern du sie nicht abschaltest. Außerdem konfiguriert das Preset die JSX-Transformation für dich.

// 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
});

Wenn du Vite ohne das Preset betreibst, ergänze den manuellen Fallback:

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

In webpack muss der Alias react-dom unterhalb von react-dom/test-utils stehen, sonst überdeckt die allgemeinere react-dom-Regel das test-utils-Mapping und die Test-Utilities werden stillschweigend auf das falsche Modul aufgelöst.

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

Installiere @rollup/plugin-alias und registriere es vor @rollup/plugin-node-resolve, damit die Umschreibungen erfolgen, bevor Rollup die Module auflöst.

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 (kein Bundler-Alias)

Node-Runtimes ignorieren Bundler-Aliase, Next.js eingeschlossen. Der Alias gehört daher in die package.json und nutzt das veröffentlichte Paket @preact/compat. Dieses gescopte Paket existiert nur, damit npms eingebautes Aliasing ein Ziel hat; seine einzige Aufgabe ist es, preact/compat unverändert zu re-exportieren. Beachte: Das gescopte @preact/compat ist nicht das eingestellte, ungescopte preact-compat.

{
  "dependencies": {
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat"
  }
}

Jest

Jest schreibt Modulpfade über Regex-Einträge unter moduleNameMapper um:

{
  "moduleNameMapper": {
    "^react$": "preact/compat",
    "^react-dom/test-utils$": "preact/test-utils",
    "^react-dom$": "preact/compat",
    "^react/jsx-runtime$": "preact/jsx-runtime"
  }
}

TypeScript

TypeScript löst Typen unabhängig von deinem Bundler auf. Bilde die Pfade daher in der tsconfig.json ab und aktiviere skipLibCheck. Aktiviere skipLibCheck deshalb, weil einige React-Bibliotheken auf Typen setzen, die compat nicht mitliefert – ein vollständiger Durchlauf über jede .d.ts in node_modules würde an diesen Deklarationen scheitern.

{
  "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/*"]
    }
  }
}

Eine minimale Migrations-Anleitung

Die Migration einer bestehenden React-App auf Preact mit moderner Tooling-Umgebung ist eine Angelegenheit in vier Schritten:

  1. Abhängigkeiten austauschen. Entferne react, react-dom und deren @types – Preact bringt eigene TypeScript-Typen mit –, dann npm install preact und npm install -D @preact/preset-vite.
  2. Preset ergänzen. Trage preact() in deine Vite-Plugins ein. Es kümmert sich sowohl um die JSX-Transformation als auch um das Alias react → preact/compat. Dadurch kannst du sämtliche manuelle esbuild.jsxInject- / jsxFactory-Konfiguration aus älteren Vite-2-Setups löschen.
  3. Render-Einstiegspunkt anpassen. Ersetze den Mount-Aufruf von React DOM durch Preacts render:
// Before
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));

// After
import { render } from 'preact';
render(<App />, document.getElementById('root'));
  1. Bauen und Größe prüfen. Genau darum geht es: Preact Core plus preact/compat landen bei etwa 9,5 KB min+gzip, gegenüber rund 60 KB für React 19 plus React DOM – nahezu alles davon im Entry react-dom/client. Für eine App, deren Komponenten ohnehin über compat aufgelöst werden, ist dieser Unterschied praktisch geschenkt.

Wann bricht preact/compat?

Compat deckt die öffentliche API von React ab, nicht deren Interna. Bibliotheken, die auf private interne react-dom-Pfade zugreifen oder auf einige der neueren React-19-APIs setzen, können auch bei korrektem Alias brechen – prüfe daher jede Abhängigkeit, bevor du ausrollst. Typ-Diskrepanzen aus React-typisierten Bibliotheken sind zu erwarten und werden durch skipLibCheck abgefangen; auf Laufzeitfehler solltest du dagegen achten. Session Replays solcher Integrationen zeigen sie häufig als Konsolenfehler, die aus dem Inneren einer Abhängigkeit statt aus deinem eigenen Code geworfen werden.

Ein kurzer Pre-Flight-Check, bevor du dich auf eine Bibliothek festlegst:

  • Durchsuche das Paket (grep) nach tiefen internen react-dom/-Importen – das häufigste Warnsignal für Bruchstellen.
  • Prüfe auf React-19-exklusive APIs, von denen die Bibliothek abhängt; gleiche die Abdeckung mit dem aktuellen Preact-Release ab, statt sie vorauszusetzen.
  • Führe die eigene Testsuite der Bibliothek unter dem oben gezeigten Jest-moduleNameMapper aus, um Fehler früh zu erkennen.
  • Mach einen Smoke-Test im Dev-Modus und behalte die Konsole auf Fehler im Auge, die aus der Abhängigkeit stammen.

SSR- und Framework-Nutzer treffen auf eine eigene Fehlerklasse: Da Bundler-Aliase in Node nicht greifen, benötigen Next.js und ähnliche Runtimes den package.json-Alias. Zudem kann Vites ssrLoadModule-Pfad manche Alias-Konfiguration umgehen – stelle also sicher, dass sowohl Client als auch Server auf preact/compat auflösen.

Aliasiere die vier Einträge für deine Toolchain, lass die Tests deiner Abhängigkeiten durch dasselbe Mapping laufen, und du kannst den Großteil des React-Ökosystems mit einem Bruchteil der Bytes weiterverwenden. Die ehrlichen Ausnahmen sind Bibliotheken, die an Reacts Interna statt an dessen öffentliche API gekoppelt sind. Fang damit an, @preact/preset-vite in einem Branch hinzuzufügen und dein Production-Bundle vorher und nachher zu messen.

FAQs

Was ist der Unterschied zwischen @preact/compat und dem alten Paket preact-compat?

Das gescopte @preact/compat ist ein aktives npm-Paket, das preact/compat re-exportiert. Es dient ausschließlich dazu, react über die package.json in Node-Runtimes wie Next.js zu aliasieren, wo Bundler-Aliase nicht greifen. Das ungescopte preact-compat ist ein anderes, archiviertes Paket, dessen Repository seit Dezember 2021 schreibgeschützt ist; es zielte auf Preact 8.x, während Preact X compat inzwischen im Core mitliefert. Installiere das ungescopte Paket niemals.

Muss ich resolve.alias weiterhin manuell schreiben, wenn ich @preact/preset-vite verwende?

Nein. Mit @preact/preset-vite erfolgt das Aliasing von react und react-dom auf preact/compat automatisch, gesteuert über die Option reactAliasesEnabled, die aktiv ist, sofern du sie nicht abschaltest. Wenn du preact() in deine Vite-Plugins einträgst, sind sowohl die JSX-Transformation als auch das Aliasing abgedeckt – ein handgeschriebenes resolve.alias ist damit redundant und kann zu Konflikten führen. Den manuellen Alias-Block mit vier Einträgen schreibst du nur, wenn du Vite ohne das Preset betreibst.

Warum werden meine Test-Utilities nach dem Aliasing in webpack auf das falsche Modul aufgelöst?

Weil der Alias react-dom in deiner webpack-Konfiguration oberhalb von react-dom/test-utils steht. Webpack greift zuerst auf die allgemeinere react-dom-Regel zu, überdeckt damit das spezifischere test-utils-Mapping, und die Test-Utilities werden stillschweigend auf preact/compat statt auf preact/test-utils aufgelöst. Behebe das, indem du den react-dom-Eintrag unter react-dom/test-utils platzierst. Für Rollup gilt eine verwandte Reihenfolgeregel: @rollup/plugin-alias gehört vor @rollup/plugin-node-resolve.

Warum bricht eine React-Bibliothek, obwohl mein preact/compat-Alias korrekt ist?

Weil compat die öffentliche API von React abbildet, nicht deren Interna. Bibliotheken, die tiefe interne react-dom-Pfade importieren oder auf die neuesten React-19-APIs angewiesen sind, können selbst bei korrektem Alias zur Laufzeit scheitern. Das zeigt sich als Konsolenfehler, die aus dem Inneren der Abhängigkeit und nicht aus deinem eigenen Code geworfen werden. Bevor du dich auf eine Bibliothek festlegst, durchsuche sie per grep nach tiefen react-dom/-Importen und führe ihre eigene Testsuite unter dem Jest-moduleNameMapper aus.

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.