Next.js Component Library im App Router einrichten

Die meisten Anleitungen für eine Next.js Component Library hören beim Installationsbefehl auf. Die Fragen kommen aber direkt danach: Welche Dateien brauchen "use client"? Warum kann eine Server-Seite kein onClick an einen Button geben? Warum flackert das dunkle Theme beim Neuladen? Und warum tut ein Theme Toggle in einer frischen App gar nichts? Dieser Guide beantwortet zuerst diese Fragen. Danach geht er ein komplettes Next.js Component Library Setup mit echten Befehlen durch, von create-next-app bis zum Update der Komponenten Monate später.
Alles hier haben wir am 13. Oktober 2026 ausgeführt, mit create-next-app@latest (Next.js 16.4.0, React 19.3.0, Tailwind CSS v4), der wingo-ui CLI 1.0.3 und next-themes 0.4.6. Die Bibliothek ist unsere eigene, Wingo UI, aber Teil 1 gilt für jede Bibliothek, deren Quellcode dir gehört, auch für ein shadcn Next.js App Router Projekt. Wie die Tokens im Detail funktionieren, erklärt der Pillar-Artikel zu Tailwind v4 Design Tokens.
Was macht eine Komponentenbibliothek fit für den App Router?
Vier Eigenschaften. Hat eine Bibliothek sie, steht dir der App Router nicht im Weg. Fehlen sie, schreibst du am Ende Wrapper.
"use client" genau in der Datei, die es braucht
Im App Router ist jede Datei eine Server Component, bis sie etwas anderes sagt. Eine Bibliothek aus use client Komponenten markiert jede interaktive Datei ganz oben. So liegt die Grenze in der Bibliothek, und deine Seiten bleiben Server Components. Jede Datei von Wingo UI, die State, Effects oder Context nutzt, beginnt mit der Direktive, darunter Button, Card, Dialog und der UIProvider in lib/ui-config.tsx.
Daraus ergibt sich eine Regel, die du dir merken solltest: Eine Server-Seite kann diese Komponenten rendern und ihnen Strings, Zahlen, Objekte und JSX übergeben, aber keine Funktionen. <Button onClick={...}> in einer Server-Seite schlägt fehl, weil React keine Funktion vom Server an den Browser schicken kann. Verschieb den Handler in eine kleine Client-Datei. Schritt 6 zeigt das Muster.
Kein window auf Modulebene
Beim Server-Rendering wird jedes Modul auf dem Server importiert, und dort gibt es weder window noch document noch matchMedia. Eine Bibliothek, die diese Objekte beim Laden des Moduls oder während des Renderns liest, stürzt ab oder rendert auf beiden Seiten etwas anderes. Unsere hält sich an eine Regel: kein window und kein document auf Modulebene oder während des Renderns. Browser-Werte werden in Effects oder in useSyncExternalStore mit einem Server-Fallback gelesen. Deshalb gibt useIsMobile() auf dem Server false zurück und aktualisiert sich nach der Hydration. Der Guide zum React Use Mobile Hook zeigt, warum das fürs Layout wichtig ist.
Ein Theme ohne Flackern
Der Server weiß nicht, ob jemand Dark Mode gewählt hat, denn diese Wahl liegt in localStorage. Die Lösung ist ein winziges Script, das die Klasse auf <html> vor dem ersten Paint setzt. next-themes fügt dieses Script für dich ein. React sieht dann ein <html>-Element, das vom Server-HTML abweicht, und deshalb trägt das Root-Layout suppressHydrationWarning. Mehr dazu in Schritt 5 und in unseren Guides zu Next.js und Tailwind CSS.
Tokens in CSS, nicht in einer JavaScript-Runtime
Farben, Radien, Schatten und Animationszeiten sind CSS-Variablen in globals.css, die über @theme inline auf Utilities abgebildet werden. Dark Mode tauscht die Variablen unter .dark. Kein JavaScript muss laufen, damit klar ist, was bg-surface bedeutet. Server Components rendern also die richtigen Farben, und es gibt keinen Theme Provider, auf den du warten musst. Deshalb lassen sich Next.js Tailwind v4 Komponenten so leicht themen: Du bearbeitest eine einzige Datei.
Wie richte ich eine Next.js Component Library Schritt für Schritt ein?
1. App anlegen
Überspring das in einem bestehenden Projekt, das schon mit Tailwind CSS v4 läuft.
Mit 16.4.0 legt der Befehl eine app/globals.css mit @import "tailwindcss";, eine tsconfig.json mit dem Alias @/* und eine dunkle Palette in @media (prefers-color-scheme: dark) an.
2. Den dunklen Media-Block des Starters entfernen
Diesen Schritt übersieht man leicht. Findet wingo-ui init einen prefers-color-scheme-Block, respektiert es ihn und legt die dunklen Werte der Bibliothek ebenfalls dort ab. Das passt für eine App, die nur der Systemeinstellung folgt. next-themes schaltet aber eine .dark-Klasse um, und eine Media Query ignoriert diese Klasse. Lösch diesen Block aus app/globals.css, bevor du init ausführst:
Ohne den Block schreibt init stattdessen einen .dark-Block und @custom-variant dark (&:is(.dark *));. Wir haben init in beiden Varianten ausgeführt: Mit dem Block meldete es dark mode: media, ohne ihn dark mode: class. Die Theming-Doku beschreibt beide Modi.
3. init ausführen
Die CLI erkennt das Framework anhand der package.json, den Alias @/* anhand der tsconfig.json und das Stylesheet, das Tailwind importiert. Dann:
- installiert sie
clsxundtailwind-mergeund legtlib/utils.tsmit dem Helpercnan, - führt sie die Design Tokens in
app/globals.csszusammen und ergänzt nur Namen, die du noch nicht hast, sodass--backgroundund--foregroundaus dem Starter bleiben, - schreibt sie
wingo-ui.jsonmit Framework, Aliassen, Stylesheet und Paketmanager.
Unser Durchlauf gab added 72 variables, 46 dark values, 72 @theme mappings, @custom-variant dark, base border color (dark mode: class) aus. Mit --yes überspringt sie die Abfrage, ob du einen Coding-Agenten verbinden willst.
4. Komponenten hinzufügen
Die Dateien landen in components/ui, hooks und lib. Der Befehl holt alles, was die drei Items importieren: den Drawer, zu dem Dialog auf Smartphones wird, lib/motion.ts, lib/tones.ts, lib/ui-config.tsx und vier Hooks. Außerdem installiert er die npm-Pakete (@radix-ui/react-dialog, motion, vaul, lucide-react und ein paar weitere). Kostenlose Items brauchen weder Account noch Key.
wingo-ui.json speichert jetzt jedes Item mit seiner Version und einem SHA-256-Hash pro Datei, und die CLI legt die installierten Kopien in .wingo-ui/ ab. Committe beides: Damit kann update neue Releases in Dateien mergen, die du bearbeitet hast.
5. Das Root-Layout mit den Providern umschließen
Installier next-themes und ersetz dann app/layout.tsx:
Das Layout bleibt eine Server Component. ThemeProvider und UIProvider sind Client Components, und die Props, die sie bekommen, sind reine Daten. Das ist also erlaubt.
attribute="class"sorgt dafür, dass next-themesclass="dark"auf<html>setzt. Danach richten sich die.dark-Tokens und diedark:-Variante.suppressHydrationWarningist nötig, weil das Theme-Script<html>ändert, bevor React hydriert. Es gilt nur für die Attribute dieses einen Elements, eine Ebene tief, und versteckt daher keine echten Mismatches in deinen Seiten.disableTransitionOnChangeverhindert, dass alle Farbübergänge gleichzeitig animieren, wenn das Theme wechselt.UIProvidersetzt app-weite Defaults pro Komponentenname und die Locale für Datums- und Zahlenformate. Hier ist jeder Button voll abgerundet, solange ein Buttonradiusnicht selbst setzt; explizite Props gewinnen immer.
6. Eine Client-Komponente in einer Server-Seite nutzen
Seiten bleiben asynchrone Server Components, die Daten laden. Komponenten ohne Handler renderst du direkt von der Seite aus, und JSX darf durch Props wandern. Ein ganzer Dialog mit Trigger und Footer funktioniert also ohne eigene Client-Datei:
Der Teil mit State und onClick kommt in eine eigene Datei:
Beide Dateien bestehen tsc --noEmit und ESLint mit der Konfiguration, die create-next-app erzeugt. Der Dialog unten ist dieselbe Komponente mit einem Formular darin. Probier ihn auch auf einem schmalen Bildschirm aus.
$ npx wingo-ui@latest add dialog7. Einen Theme Toggle ergänzen
Theme Toggle ist eine Pro-Komponente: ein animierter Button mit Sonne und Mond, ein Segmented Control oder ein Dropdown, alle auf Basis von next-themes. Im kostenlosen Tarif reicht eine kurze Client-Datei:
Die Icons wechseln per CSS. Das Server-HTML und der erste Client-Render stimmen also überein, und du brauchst kein mounted-Flag. Die häufigsten Ursachen für die Hydration-Warnung behandelt unser Fix für den next-themes Hydration Error, die Variante und die Tokens hinter dem Toggle unser Guide zu Tailwind v4 Dark Mode. Weitere Artikel findest du unter Next.js und Tailwind CSS.
8. Für Pro-Komponenten anmelden
Pro-Items brauchen einen Key. Melde die CLI einmal pro Rechner an:
Sie öffnet den Browser, um das Gerät freizugeben, und speichert einen Key unter ~/.config/wingo-ui. Auf einem Server oder in der CI übergibst du stattdessen einen Key aus deinem Account:
Ohne Key bricht das Hinzufügen eines Pro-Items mit einer Meldung ab, die das Item nennt und auf die Tarife verlinkt. Die Tarife stehen auf der Preisseite.
9. Komponenten aktuell halten
Wir haben ein Label in einer installierten Datei geändert und diff ausgeführt: Es gab einen Unified Diff unserer Änderung gegen upstream aus. update macht einen Three-Way-Merge mit der Kopie in .wingo-ui/, behält deine Änderungen und markiert echte Konflikte wie git. Die CLI-Doku listet jedes Flag auf.
Was prüfe ich, wenn etwas falsch aussieht?
Wie geht es weiter?
Fang mit den kostenlosen Basics an: npx wingo-ui@latest add button dialog card. Danach folgst du der Installations-Doku für die shadcn CLI und den manuellen Weg. Wenn du UI mit Claude Code, Codex oder Cursor schreibst, verbinde den MCP-Server, damit der Agent die Bibliothek durchsucht, bevor er eine Komponente schreibt. Du baust mit Vite statt mit Next.js? Das shadcn Vite Setup zeigt dieselben Schritte dort.
Häufige Fragen
Kann ich eine Client-Komponentenbibliothek in Next.js Server Components nutzen?
Ja. Eine Server-Seite kann eine Datei mit use client importieren, rendern und ihr Strings, Zahlen und JSX übergeben. Funktionen wie onClick kann sie nicht übergeben, also steckst du Handler in eine kleine Client-Komponente und renderst diese in der Seite.
Warum braucht das Root-Layout suppressHydrationWarning?
Bevor React hydriert, gibt ein Script von next-themes dem html-Element die Klasse dark oder light. Das Server-HTML hatte diese Klasse nicht, also würde React einen Mismatch melden. Das Attribut unterdrückt die Warnung nur für dieses eine Element.
Warum wechselt mein Theme Toggle nach wingo-ui init die Farben nicht?
Das Stylesheet von create-next-app bringt einen Block @media (prefers-color-scheme: dark) mit, und init legt die dunklen Werte dort ab, wenn es einen findet. Damit die Klasse von next-themes gewinnt, löschst du den Block vor init oder verschiebst die dunklen Werte in einen .dark-Block und ergänzt @custom-variant dark (&:is(.dark *));
Brauchen die kostenlosen Komponenten von Wingo UI einen Account oder API-Key?
Nein. Kostenlose Items wie button, card und dialog installierst du mit npx wingo-ui@latest add, ganz ohne Key. Pro-Items brauchen einmal pro Rechner npx wingo-ui@latest login oder WINGO_UI_API_KEY in der CI.
Funktioniert dieses Setup mit Next.js 15?
Wir haben jeden Schritt auf Next.js 16.4.0 ausgeführt. Die Bibliothek braucht React 19 und Tailwind CSS v4, beides geht auch in einem Next.js-15-Projekt mit App Router. Dort haben wir die Schritte aber nicht wiederholt.
- Next.js
- App Router
- Setup
- Installation
- Tailwind CSS v4


