Eine JavaScript-Bibliothek mit Rslib veröffentlichen
Veröffentlichen Sie JavaScript-Bibliotheken mit Rslib: Richten Sie ESM- und CJS-Builds ein, erzeugen Sie TypeScript-Deklarationen, liefern Sie Styles mit und prüfen Sie Fehler in CI.
Rslib ist ein Build-Tool für Bibliotheken auf Basis von Rsbuild und Rspack. Aus einer einzigen Konfiguration erzeugt es ESM- und CommonJS-Output, TypeScript-Deklarationen, framework-kompilierte Komponenten und verarbeitete Assets. Es richtet sich an Pakete, die Sie auf npm veröffentlichen, nicht an Apps, die Sie deployen.
Dieser Unterschied ist wichtig. Ein App-Bundle muss nur in einem einzigen Browser-Tab laufen. Eine Bibliothek wird dagegen von fremden Bundlern, Node.js-Versionen und TypeScript-Einstellungen importiert. Sie geht dabei auf eine Weise kaputt, die Ihre eigene Test-App nie zeigt: ein fehlschlagendes require(), Typen, die zu any aufgelöst werden, oder eine Komponente, die ohne ihre Styles ausgeliefert wird. Dieser Leitfaden begleitet ein Paket vom ersten Build bis zur Prüfung vor der Veröffentlichung in der CI und verwendet dabei Rslib 1.0, erschienen am 3. September 2026.
Das Wichtigste in Kürze
- Rslib 1.0 bietet eine stabile Konfiguration und JavaScript-API, die Semantic Versioning folgen, und setzt Node.js 20.19+ oder 22.12+ voraus.
- Die Rslib-Dokumentation empfiehlt für neue Pakete reines ESM. Ein dualer ESM/CJS-Build ist nur dann sinnvoll, wenn ein bestimmter Consumer kein ESM laden kann.
- Unter Node.js
^20.19.0oder>=22.12.0kann CommonJS-Code ein reines ESM-Paket perrequire()laden. Voraussetzung ist, dass kein geladenes Modul, einschließlich des Einstiegspunkts, Top-Level-awaitverwendet. - Der Bundle-Modus (
bundle: true) eignet sich für SDKs, CLIs und Node.js-Utilities. Der Bundleless-Modus (bundle: false) erhält die Dateistruktur des Quellcodes und eignet sich für Komponentenbibliotheken. rsbuild-plugin-publintundrsbuild-plugin-arethetypeswronglassen sich so konfigurieren, dass sie nur in der CI innerhalb vonrslib buildlaufen. Packaging-Fehler lassen den Build dann schon vornpm publishfehlschlagen.
Warum ist das Veröffentlichen einer Bibliothek schwieriger als das Bauen einer App?
Bei einem veröffentlichbaren Paket müssen vier Dinge gleichzeitig stimmen. Es braucht ESM- und gegebenenfalls CJS-Output, der in jeder unterstützten Runtime lädt. Es braucht Deklarationsdateien, die unter jeder moduleResolution-Einstellung der Consumer aufgelöst werden. Komponenten müssen mit ihrem CSS ausgeliefert werden. Und das exports-Feld in der package.json muss für jede Condition auf eine Datei verweisen, die tatsächlich existiert.
Jede dieser Aufgaben übernimmt in der Regel ein anderes Tool: ein Bundler für das JavaScript, tsc für die Deklarationen, ein PostCSS-Schritt für die Styles und eine handgeschriebene exports-Map, die alles zusammenhält. Sobald eines dieser Tools einen Ausgabepfad ändert, ist das Paket kaputt.
Was ist Rslib?
Rslib ist ein Build-Tool für Bibliotheken, das alle vier Aufgaben in einem einzigen Build abdeckt. Ein lib-Array definiert die einzelnen Ausgabeformate. Die Option dts erzeugt Deklarationen. Im Bundleless-Modus korrigiert sie darin außerdem Pfad-Aliase und Import-Endungen, damit sie zu den erzeugten JavaScript-Dateien passen. Wie CSS verarbeitet wird, hängt vom Build-Modus ab. Publint findet Fehler im exports-Feld. Rslib schreibt Ihr exports-Feld also nicht für Sie, sondern prüft das, das Sie selbst geschrieben haben.
Ein minimaler Rslib-Build von Anfang bis Ende
Am schnellsten starten Sie, indem Sie ein Projekt per Scaffolding anlegen oder einen einzelnen CLI-Befehl ausführen. Bei einem bestehenden Paket fügen Sie eine Konfigurationsdatei mit einem lib-Eintrag pro Format hinzu. Laut Quick Start benötigt Rslib v1 Node.js 20.19+ oder 22.12+.
npm create rslib@latest
# or, in an existing package
npm add @rslib/core -D
Für einen Build mit nur einem Format ist keine Konfigurationsdatei nötig:
npx rslib build --entry src/index.ts --format esm --dts
--entry legt den Einstiegspunkt im Quellcode fest, --format esm wählt das Modulformat und --dts erzeugt Deklarationen. Für dualen Output legen Sie eine rslib.config.ts an:
// rslib.config.ts
import { defineConfig } from '@rslib/core';
export default defineConfig({
lib: [
{ format: 'esm', dts: true },
{ format: 'cjs', dts: true },
],
syntax: 'es2023',
});
Alles, was Sie auf oberster Ebene festlegen, gilt für jeden Output. Rslib führt diese Einstellungen mit jedem lib-Eintrag zusammen, sodass jeder Eintrag nur die Abweichungen enthalten muss. Benötigen Sie lediglich einen Standard-ESM-Output, können Sie lib in Rslib 1.0 auch ganz weglassen. Fügen Sie anschließend ein Build-Skript hinzu:
{ "scripts": { "build": "rslib build" } }
Nach dem Build liegt der Output standardmäßig im Verzeichnis dist. Es enthält einen ESM-Build, einen CJS-Build und Deklarationsdateien. Die Dateiendungen richten sich nach lib.autoExtension. Mit "type": "module" erhält ESM-Output die Endung .js und CJS-Output die Endung .cjs. Ohne diese Angabe erhält CJS .js und ESM .mjs. Standardmäßig können beide Formate Deklarationen mit demselben .d.ts-Namen erzeugen. Über die dts-Optionen steuern Sie die Deklarationsendungen pro Format. Lassen Sie in Ihrer exports-Map die import-Condition auf den ESM-Einstiegspunkt und die require-Condition auf den CJS-Einstiegspunkt verweisen, jeweils mit passenden Deklarationen. Übernehmen Sie die tatsächlichen Pfade aus Ihrem eigenen dist, statt sie zu raten.
Bei ESM- und CJS-Output bündelt Rslib standardmäßig weder dependencies noch optionalDependencies oder peerDependencies. Eine Peer-Dependency wie React bleibt also extern. devDependencies werden hingegen gebündelt.
Nur ESM oder dual ESM/CJS veröffentlichen?
Der Leitfaden zu Ausgabeformaten von Rslib empfiehlt, neue Pakete als reines ESM zu veröffentlichen und CJS nur dann hinzuzufügen, wenn ein Consumer es eindeutig benötigt. Dualer Output kostet mehr als nur einen zweiten Build-Schritt. Sie müssen zwei Sätze von exports-Einträgen korrekt halten und doppelt so viele Setups testen. Lädt ein Consumer beide Formate, erhält er außerdem zwei Instanzen Ihres Pakets. Das kann gemeinsamen State und identitätsbasierte Prüfungen wie instanceof brechen.
Viele CommonJS-Consumer brauchen gar keinen CJS-Build mehr. Unter Node.js ^20.19.0 oder >=22.12.0 können sie ein reines ESM-Paket direkt laden. Dafür müssen zwei Bedingungen erfüllt sein: Ihr exports-Feld muss einen require()-Aufruf zum ESM-Einstiegspunkt durchlassen, und kein Modul im geladenen Graphen darf Top-Level-await verwenden:
const utils = require('my-esm-only-package');
Veröffentlichen Sie dualen Output nur dann, wenn ein Teil Ihrer CommonJS-Nutzer eine Node-Version, ein Tool oder eine Runtime verwendet, die ESM nicht über require() laden kann, oder wenn Nutzer ausdrücklich eine separate CJS-Datei verlangen. Die Node.js-Paketbeispiele zeigen die passende package.json-Konfiguration für beide Varianten.
Prüfungen vor der Veröffentlichung
Zwei Rsbuild-Plugins finden Packaging-Fehler in einem Rslib-Build, bevor es npm tut. rsbuild-plugin-publint sucht nach typischen Fehlern in Ihrer package.json, Ihrer Dateistruktur und Ihrem exports-Setup. rsbuild-plugin-arethetypeswrong testet, ob Ihre Typen auch dann noch laden, wenn ein Consumer eine andere Modulauflösung verwendet. Die Ankündigung von Rslib 1.0 zeigt, wie beide nur dann laufen, wenn CI gesetzt ist:
// rslib.config.ts
import { defineConfig } from '@rslib/core';
import { pluginAreTheTypesWrong } from 'rsbuild-plugin-arethetypeswrong';
import { pluginPublint } from 'rsbuild-plugin-publint';
export default defineConfig({
lib: [
{ format: 'esm', dts: true },
{ format: 'cjs', dts: true },
],
plugins: [
pluginPublint({ enable: Boolean(process.env.CI) }),
pluginAreTheTypesWrong({ enable: Boolean(process.env.CI) }),
],
});
Installieren Sie beide als Dev-Dependencies. Lokale Builds bleiben so schnell, und der rslib build-Schritt, den Sie ohnehin in der CI ausführen, wird zur Qualitätsschranke vor der Veröffentlichung.
Auch die Erzeugung von Deklarationen wird bei großen Projekten schneller. Ab TypeScript 7 verwendet Rslib automatisch tsgo und führt trotzdem eine Typprüfung durch. Die experimentelle Option dts.isolated ist noch schneller, überspringt aber die Typprüfung. Führen Sie in diesem Fall parallel einen separaten Typprüfungsschritt aus. In einem eigenen Test des Rslib-Teams am Rsbuild-Repository dauerte die Deklarationserzeugung mit TypeScript 6 9,7 s, mit TypeScript 7 4,1 s und mit isolierten Deklarationen 2,3 s. Die Ergebnisse in Ihrem eigenen Projekt können abweichen.
Komponentenbibliotheken und Bundleless-Modus
Verwenden Sie den Bundleless-Modus für Komponentenbibliotheken und den Bundle-Modus für SDKs und CLIs. Mit bundle: false erzeugt Rslib aus jeder Quelldatei eine eigene Ausgabedatei in derselben Ordnerstruktur wie Ihr Quellcode. Tree Shaking und On-Demand-Loading übernimmt dann der Bundler des Consumers. Die Compiler für React, Vue, Svelte und Solid werden über Rsbuild-Plugins eingebunden, und create-rslib enthält für jedes Framework ein Template.
// rslib.config.ts
import { pluginReact } from '@rsbuild/plugin-react';
import { defineConfig } from '@rslib/core';
export default defineConfig({
bundle: false,
output: { target: 'web' },
plugins: [pluginReact()],
});
Komponenten importieren Styles wie gewohnt:
import './button.css';
import styles from './button.module.css';
CSS Modules und PostCSS funktionieren ohne zusätzliche Konfiguration. Wie Rslib Styles ausgibt, hängt vom Build-Modus ab. Sass, Less, Stylus und Tailwind CSS werden über Rsbuild-Plugins hinzugefügt. Bibliotheken für Vue, Svelte und Solid verwenden dieselbe Konfigurationsstruktur mit dem jeweils passenden Plugin. In der Ankündigung von Rslib 1.0 zeigt ein Benchmark mit 10.000 React-Komponenten, dass Version 1.0 die Build-Zeit gegenüber 0.7.0 ohne Cache um etwa 24,3 % und mit Cache um etwa 56,7 % senkt. Der Output ist vor gzip etwa 32,2 % und nach gzip 4,1 % kleiner.
Module-Federation-Output
Mit format: 'mf' kann Rslib eine Bibliothek auch als Module-Federation-Remote bauen. Mehrere Anwendungen laden sie dann zur Laufzeit, statt sie von npm zu installieren. Setzen Sie diese Option ein, wenn das unabhängige Deployment von gemeinsam genutztem Code wichtiger ist als versionierte Paket-Releases.
Rslib vs. tsup vs. Vite Library Mode
Rslib, tsup und der Vite Library Mode unterscheiden sich eher im Funktionsumfang als in der Geschwindigkeit. tsup basiert auf esbuild und konzentriert sich auf das Bündeln von TypeScript. Der Vite Library Mode richtet ein Build-Tool für Anwendungen auf einen Bibliotheks-Einstiegspunkt aus. Rslib ist eigens für Bibliotheken entwickelt und baut auf Rsbuild und Rspack auf. Eine einzige Konfiguration deckt Ausgabeformate, Bundle- und Bundleless-Modus, Deklarationen, Framework-Kompilierung, Assets und Prüfungen vor der Veröffentlichung ab. Nutzt Ihre App bereits Rsbuild oder Rspack, kann die Bibliothek deren Plugins und Konfiguration mitverwenden.
Der Artikel zum aktuellen Stand der JavaScript-Bundler zeigt, wo Rspack im größeren Ökosystem steht. Wenn Sie Ihre eigene Pipeline lieber aus einem universellen Bundler und Plugins zusammenstellen, beginnen Sie mit diesem Leitfaden zum Einstieg in Rollup. Für Migrationen bietet Rslib einen Leitfaden für den Umstieg von tsc, und der Quick Start verlinkt einen entsprechenden Leitfaden für tsup.
Fazit
Eine Bibliothek ist erst dann bereit zur Veröffentlichung, wenn Formate, Deklarationen, Styles und exports-Map zueinander passen. Rslib baut und prüft all das gemeinsam. Beginnen Sie mit reinem ESM, fügen Sie dem lib-Array nur dann einen CJS-Eintrag hinzu, wenn ein Consumer ihn benötigt, und aktivieren Sie publint und arethetypeswrong in der CI vor Ihrem nächsten Release.
FAQs
Wie bündle ich eine Dependency in den Rslib-Output, statt sie extern zu lassen?
Setzen Sie output.autoExternal innerhalb des lib-Eintrags und deaktivieren Sie das Dependency-Feld, das gebündelt werden soll, zum Beispiel indem Sie dependencies auf false setzen. Rslib v1 markiert lib.autoExternal zugunsten von output.autoExternal aus Rsbuild als veraltet, die alte Option funktioniert aber weiterhin. Die automatische Externalisierung greift nur im Bundle-Modus. Im Bundleless-Modus werden Dependencies nie gebündelt. Um nur bestimmte Pakete extern zu halten, tragen Sie diese in output.externals ein.
Kann Rslib alle TypeScript-Deklarationen in einer einzigen Datei bündeln?
Ja. Installieren Sie @microsoft/api-extractor als Dev-Dependency und setzen Sie dts.bundle im lib-Eintrag auf true. API Extractor übernimmt das Bündeln der Deklarationen. Standardmäßig ist dts.bundle auf false gesetzt. Die Deklarationen direkter Dependencies, die nicht externalisiert sind, werden automatisch inline eingebunden. Mit dts.bundle.bundledPackages ersetzen Sie diese Standardliste. Das ist hilfreich, wenn Sie transitive Dependencies bündeln müssen.
Kann Rslib ein UMD-Bundle für ein CDN oder einen Script-Tag erzeugen?
Ja. Setzen Sie format in einem lib-Eintrag auf 'umd' und legen Sie mit lib.umdName den globalen Namen fest, den das Bundle bereitstellt, zum Beispiel MyLibrary. Bei UMD-Output ist die automatische Externalisierung standardmäßig deaktiviert. Dependencies werden daher gebündelt, sofern Sie sie nicht in output.externals eintragen. Jeder Eintrag dort ordnet ein Paket einer globalen Browser-Variablen zu, zum Beispiel react zu React. lib.autoExtension ist bei UMD standardmäßig aktiviert.
Wie erzeuge ich .d.cts-Deklarationen für den CommonJS-Build?
Aktivieren Sie dts.autoExtension in jedem lib-Eintrag. Die Option folgt denselben Regeln wie lib.autoExtension: Ist type in der package.json auf module gesetzt, verwenden ESM-Deklarationen die Endung .d.ts und CJS-Deklarationen die Endung .d.cts. Die Option ist standardmäßig deaktiviert, sodass ESM- und CJS-Output am Ende denselben .d.ts-Dateinamen haben können. Lassen Sie die types der require-Condition auf die .d.cts-Datei verweisen.
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