Tailwind v4 Migration für shadcn: Schritt für Schritt
Du hast eine funktionierende shadcn/ui-App auf Tailwind v3, und neue shadcn-Komponenten wie die meisten aktuellen Tutorials gehen inzwischen von Tailwind v4 aus. Die Tailwind v4 Migration eines shadcn-Projekts läuft größtenteils mechanisch ab, aber die Teile, die es nicht sind, scheitern lautlos: Borders werden schwarz, Fokusringe dünner, Accordions animieren nicht mehr, und eine Farbe aus tailwind.config.js tut einfach gar nichts. Dieser Guide zeigt die Reihenfolge, der wir in einem echten Projekt folgen würden, mit dem CSS vor und nach jedem Schritt und einer Liste der Komponenten, die du von Hand nachbessern musst. Wir bauen Wingo UI, eine Komponentenbibliothek für React 19 und Tailwind v4. Fakten von außen verlinken deshalb auf ihre Quellen, geprüft am 6. November 2026.
Solltest du ein shadcn-Projekt jetzt auf Tailwind v4 migrieren?
Ja, wenn deine Nutzer aktuelle Browser haben. Tailwind v4 braucht laut Tailwind Upgrade Guide (opens in a new tab) mindestens Safari 16.4, Chrome 111 oder Firefox 128. Musst du ältere Browser unterstützen, bleib bei v3.4. Das Upgrade-Tool braucht außerdem Node.js 20 oder neuer.
Eine Deadline gibt es nicht. Die shadcn-Seite zu Tailwind v4 (opens in a new tab) nennt die Umstellung nicht breaking: Bestehende Apps mit v3 und React 18 laufen weiter, und Komponenten, die du hinzufügst, bleiben bei v3, bis du upgradest. Der Preis fürs Warten: Jede neue Komponente und jedes Beispiel, das du kopierst, ist für v4 geschrieben.
Mach die Arbeit auf einem eigenen Branch, ohne Feature-Änderungen dazwischen, damit der Diff nur die Migration zeigt.
Welche Tailwind v4 Breaking Changes treffen shadcn?
Der Upgrade Guide (opens in a new tab) listet rund zwanzig. Diese hier begegnen dir in einem shadcn-Projekt:
Die Hover-Änderung ist gut fürs Smartphone: Nach einem Tipp bleibt ein Button nicht mehr in seiner Hover-Farbe hängen. Die Cursor-Änderung fällt den meisten als Erstes auf.
Schritt 1: Wie führst du den Upgrade-Codemod aus?
Committe alles und starte dann das offizielle Tool im Projektverzeichnis:
Das Tool aktualisiert package.json, ersetzt die drei @tailwind-Direktiven durch @import "tailwindcss";, stellt PostCSS auf @tailwindcss/postcss um, wandelt deine JavaScript-Config in CSS um und benennt Utilities in deinen Quelldateien um (shadow-sm zu shadow-xs, outline-none zu outline-hidden, w-[--x] zu w-(--x)). Die shadcn-Seite zu Tailwind v4 zeigt den Befehl noch mit @next, ein Überbleibsel aus der Beta von v4; der aktuelle Tailwind Guide lässt das Tag weg.
Zwei Grenzen sind wichtig. Das Tool kann nur Klassennamen umschreiben, die es als vollständige Strings lesen kann; alles, was erst zur Laufzeit entsteht (`bg-${tone}-500`), bleibt unverändert. Und es bildet deine Tokens mit hsl(var(--x))-Wrappern in @theme ab, was funktioniert, aber umständlich ist. Schritt 2 räumt das auf. Bricht das Tool mit „Could not load the configuration file“ ab (der Fehler wurde in der shadcn-Diskussion zum Upgrade (opens in a new tab) gemeldet), prüf mit node -e "require('./tailwind.config.js')", ob die Config für sich allein lädt. Behebe, was dabei ausgegeben wird, bevor du das Tool erneut startest.
Starte danach den Dev-Server und sieh dir die App an, bevor du irgendetwas anderes änderst.
Schritt 2: Wie ziehst du tailwind.config nach @theme um?
Das ist der Kern, wenn du Tailwind v3 auf v4 upgraden willst. Ein shadcn-Projekt mit v3 hält die Rohwerte in globals.css und die Zuordnung zu Utilities in tailwind.config.js:
In v4 gibt es keine separate Tailwind v4 Config-Datei mehr: Beide Hälften wandern ins CSS. Die shadcn-Doku nennt vier Änderungen zusätzlich zur Ausgabe des Codemods: :root und .dark aus @layer base herausholen, jeden Wert in hsl() setzen, @theme zu @theme inline ändern und die hsl()-Wrapper darin entfernen. Das Ergebnis:
Warum inline wichtig ist: Mit einfachem @theme wird bg-background zu var(--color-background) kompiliert, und diese Variable wird dort aufgelöst, wo sie definiert ist, auf :root. Eine .dark-Klasse unterhalb der Wurzel oder ein Theme, das nur für einen Abschnitt gilt, ändert sie nie. Mit @theme inline schreibt Tailwind var(--background) direkt in die Utility, der Wert wird also auf dem Element aufgelöst, das sie nutzt. In der shadcn-Diskussion zum Upgrade berichtete jemand am 28. Dezember 2024, dass der Dark Mode erst nach dem Wechsel auf @theme inline funktionierte. Der Guide zum Tailwind v4 Dark Mode behandelt die Klassen-Variante und next-themes im Detail.
Aus darkMode: ["class"] wird die @custom-variant-Zeile. Die Regel * { @apply border-border; } kompiliert erst, wenn --color-border in @theme existiert; „Cannot apply unknown utility class: border-border“ heißt, dass die Zuordnung fehlt oder noch eine alte Config geladen wird.
Sieht die Seite richtig aus, lösch tailwind.config.js und die @config-Zeile, die der Codemod eingefügt hat, und setz den Config-Pfad in components.json auf leer, wie es die Doku zu components.json (opens in a new tab) für v4 vorgibt:
Schritt 3: Solltest du hsl-Tokens in oklch umwandeln?
Nur wenn du willst. Sobald jede Variable eine vollständige Farbe enthält, funktioniert hsl() in v4 problemlos, und das Format ändert nichts am Kontrast. Die aktuelle Palette von shadcn ist oklch (das Neutral-Theme nutzt laut Theming-Doku (opens in a new tab) --background: oklch(1 0 0) und --foreground: oklch(0.145 0 0)), und die dunklen Farben wurden am 12. März 2025 für bessere Barrierefreiheit überarbeitet. Willst du diese Werte, kopier sie von der Theming-Seite, statt deine eigenen umzurechnen, und lass deine Markenfarbe, wie sie ist.
Zwei Dinge musst du jetzt ändern, weil die Variablen vollständige Farben enthalten:
Such außerdem in deinem Code nach hsl(var(-- in Inline-Styles, SVG-Fills und color-mix()-Aufrufen. Jede dieser Stellen packt jetzt eine Farbe in ein zweites hsl() und rendert gar nichts. Ein shadcn Tailwind v4 Theme, dessen Tokens vollständige Farben sind, lässt sich auch leichter erweitern: color-mix(in oklab, var(--primary) 10%, transparent) funktioniert überall. Unser Guide zu Tailwind v4 Design Tokens geht weiter, mit Flächen, Status-Tones und einem Kontrast-Skript.
Schritt 4: Wie reparierst du Borders und Fokusringe?
Such nach jedem border, border-t, divide-y und ring ohne Farbe. Die Basisregel aus Schritt 2 repariert Borders global, deshalb sehen die meisten shadcn-Komponenten nach dem Upgrade richtig aus. Alles, was außerhalb der Reichweite deines Stylesheets gerendert wird (ein Portal in die Wurzel einer Fremdbibliothek, ein iframe), zeichnet weiter Borders in currentColor.
Bei Ringen ist es subtiler. Die Standardkomponenten von shadcn v3 nutzen ring-2, das seine Breite behält, aber in eigenem Code schrumpft ein ring ohne Wert von 3px auf 1px, und ein 1px-Ring taugt oft nicht als Fokusindikator. Schreib Breite und Farbe explizit aus:
Willst du den Pointer-Cursor auf allen Buttons zurück, füg das Snippet aus dem Upgrade Guide ein:
Geh danach jede Seite mit der Tastatur durch, in beiden Themes. Dieser eine Durchgang findet die meisten Regressionen bei Ringen und Outlines.
Schritt 5: Was ersetzt tailwindcss-animate?
tw-animate-css. shadcn hat tailwindcss-animate am 19. März 2025 als veraltet markiert, und neue Projekte bekommen den Ersatz standardmäßig:
Entferne @plugin "tailwindcss-animate"; aus globals.css und füg @import "tw-animate-css"; nach dem Tailwind-Import ein. Die Klassen, die deine Dialoge, Popover und Dropdowns nutzen (animate-in, fade-in-0, zoom-in-95, slide-in-from-top-2), funktionieren weiter, und das Paket bringt animate-accordion-down und animate-accordion-up mit, die Keyframes aus tailwind.config.js können also weg. Laut README (opens in a new tab) ist es kein hundertprozentig kompatibler Drop-in-Ersatz, klick also jede animierte Komponente einmal durch.
Eine andere Möglichkeit: in JavaScript animieren. Unsere Komponenten nutzen motion-Springs statt Keyframes. Beim Accordion federt die Höhe per Spring auf, und die Antwort wird eingeblendet; bei reduzierter Bewegung entfällt die Höhenanimation, und die Antwort wird nur eingeblendet. Ein Animations-Plugin, das du migrieren müsstest, gibt es gar nicht.
$ npx wingo-ui@latest add accordionWelche shadcn-Komponenten musst du von Hand nachbessern?
Bei einer shadcn Tailwind v4 Migration erledigt der Codemod die meisten Klassen-Strings. An diesen Stellen verpasst er etwas, oder die neuen Defaults ändern den Look:
- Sidebar: Sie setzt ihre Größe mit Klassen wie
w-[--sidebar-width], die v4 alsw-(--sidebar-width)schreibt. Prüf, ob der Codemod jede davon erwischt hat und ob die Sidebar noch in voller Breite aufgeht. Jemand, dessen Sidebar nach dem Upgrade kaputt war, hat das am 3. Februar 2025 gelöst, indem ersidebar.tsxundglobals.cssaus der Tailwind-v4-Beispiel-App von shadcn übernommen hat. - Radius-Klassen: Der Codemod benennt
rounded-sminrounded-xsundroundedinrounded-smum. Dein Theme bildet--radius-smselbst ab, die Umbenennung kann also den Look ändern:rounded-smbedeutete vorher--radiusminus 4px, undrounded-xsfällt auf Tailwinds Default von 2px zurück. Vergleich ein paar Inputs und Badges mit dem v3-Build und behalte die Klasse, die den Radius ergibt, den du hattest. - Accordion und Collapsible: Sie verlieren ihre Animation, wenn du die Keyframes aus der Config entfernt hast, bevor
tw-animate-cssdrin war. - Chart:
hsl(var(--chart-n))inchartConfig, siehe Schritt 3. - Toast: zugunsten von Sonner als veraltet markiert. Dein bestehender Toast läuft weiter, aber shadcn verweist neue Projekte inzwischen auf
sonner. - Alles mit
cva-Varianten aus Template-Strings: Die kann das Tool nicht lesen. Such mit grep nach`${inclassName. - Komponenten, die du angepasst hast:
npx shadcn@latest add --all --overwriteüberschreibt jede Datei mit der v4-Version. Committe vorher und spiel deine Änderungen danach aus dem Diff wieder ein. Unser Artikel zum Updaten von shadcn-Komponenten, ohne Anpassungen zu verlieren, erklärt, warum ein Three-Way-Merge dir diesen Schritt erspart.
Die restlichen v4-Änderungen von shadcn sind optional: size-4 statt w-4 h-4, data-slot auf jedem Teil und normale Funktionen statt forwardRef (React 19 übergibt ref als Prop; siehe unsere Migration von forwardRef in React 19).
Wie sieht eine Komponente aus, die für Tailwind v4 geschrieben ist?
Sie färbt über CSS-Variablen, nutzt für Touch und Fokus v4-Varianten statt JavaScript und braucht keine Config-Datei. Unser Button ist ein gutes Beispiel: Die Varianten färben über CSS-Variablen mit der neuen Kurzschreibweise (bg-(--tone), text-(--tone-foreground)), der Fokus nutzt outline-hidden plus eine 2px breite outline-ring, und die Variante pointer-coarse: vergrößert die Standardgröße auf Touchscreens auf 44px, ganz ohne JavaScript. Die --tone-*-Variablen kommen aus Tones, also ändern tone="success" oder color="#0EA5E9" jede Variante ohne neue Klassen:
$ npx wingo-ui@latest add buttonApp-weite Defaults laufen über UIProvider. Eine migrierte App kann radius oder size also einmal setzen, statt jede Datei anzufassen:
Wenn du unsere Komponenten ins migrierte Projekt holst, führ npx wingo-ui@latest init nach der Migration aus, nicht davor. Es liest den Pfad zum Stylesheet aus components.json, warnt, wenn diese Datei noch ein v3-Stylesheet ohne @import "tailwindcss" ist, und ergänzt nur die Tokens, die fehlen: Werte, die dein shadcn-Theme schon definiert, bleiben, wie sie sind. Danach holt npx wingo-ui@latest add button card den Quellcode von Card und Button in dein Projekt. Weitere Guides zu diesem Stack findest du in Next.js und Tailwind CSS.
Wie prüfst du, ob die Migration geklappt hat?
Geh diese Liste durch, bevor du mergst:
npm run buildläuft durch, und der Dev-Server zeigt keine Fehler wegen unbekannter Utilities („unknown utility“).- Jede Seite in Light und Dark: keine schwarzen Borders, keine fehlenden Hintergründe.
- Tastatur-Durchgang: Jedes Control zeigt einen sichtbaren Fokusring.
- Öffne jeden Dialog, jedes Sheet, Popover, Dropdown und Accordion: Alle animieren noch.
- Charts zeigen ihre Farben.
- Auf dem Smartphone: Buttons bleiben nach einem Tipp nicht eingefärbt, und die Sidebar geht in voller Breite auf.
git grep -n "hsl(var(--"undgit grep -n "tailwind.config"finden nichts mehr, was du noch brauchst.
Wie geht es weiter?
Lass den Codemod noch heute auf einem Branch laufen und behebe, was die Checkliste findet; bei einer typischen shadcn-App bleiben für die Handarbeit ein paar kleine Commits. Willst du Komponenten, die von Anfang an für Tailwind v4 geschrieben wurden, mit fertigen Tokens, Fokusringen und Touch-Größen, hol dir Button, Card und Accordion mit einem kostenlosen Konto (nur E-Mail, keine Karte): Sie gehören zu den 75 kostenlosen von insgesamt 357 Items im Katalog, und die Preisseite zeigt, was Pro zusätzlich bietet.
Häufige Fragen
Unterstützt shadcn/ui Tailwind v4?
Ja. Die shadcn CLI richtet neue Projekte mit Tailwind v4 und React 19 ein, und jede Komponente hat data-slot-Attribute und oklch-Farben bekommen. Bestehende Projekte mit Tailwind v3 laufen weiter, und die shadcn-Doku nennt das Upgrade nicht breaking.
Brauche ich mit Tailwind v4 noch eine tailwind.config-Datei?
Nein. Tailwind v4 liest seine Konfiguration aus dem CSS: Theme-Werte stehen in @theme, Plugins in @plugin, eigene Utilities in @utility. Eine JavaScript-Config funktioniert noch, wenn du sie mit @config lädst, sie wird aber nicht mehr automatisch erkannt, und die components.json von shadcn erwartet in v4 ein leeres Feld tailwind.config.
Warum sind meine Borders nach dem Upgrade auf Tailwind v4 schwarz?
Tailwind v4 hat die Standardfarbe für Borders von gray-200 auf currentColor geändert, eine Border-Klasse ohne Farbe übernimmt also die Textfarbe. shadcn-Projekte lösen das mit einer Basisregel, * { @apply border-border; }, die nur funktioniert, wenn --color-border in @theme inline abgebildet ist.
Was ersetzt tailwindcss-animate in Tailwind v4?
tw-animate-css, das shadcn am 19. März 2025 übernommen hat. Installier es als Dev-Dependency, entferne @plugin "tailwindcss-animate" und füg @import "tw-animate-css" nach @import "tailwindcss" ein. Es deckt animate-in, fade, zoom, slide und die Keyframes für Accordion und Collapsible ab, laut README ist es aber kein hundertprozentiger Drop-in-Ersatz.
Muss ich meine shadcn-Farben von hsl auf oklch umstellen?
Nein. hsl()-Werte funktionieren in Tailwind v4, solange jede Variable eine vollständige Farbe enthält. Stell nur auf oklch um, wenn du die aktuelle shadcn-Palette willst oder die Helligkeit leichter anpassen möchtest, und lass nach jeder geänderten Farbe einen Kontrast-Check laufen.
- Tailwind CSS
- shadcn/ui
- Migration
- Next.js
- Design Tokens

