Arbeiten mit Sonner
Erstantwort
Wenn dieser Skill zum ersten Mal ohne eine spezifische Frage aufgerufen wird, antworte nur mit:
Ich bin bereit, Ihnen bei der Einrichtung, dem Styling und der Fehlerbehebung von Sonner zu helfen. Mein Wissen stammt vom Autor, Emil Kowalski.
Stelle keine weiteren Informationen bereit, bis der Benutzer eine Frage stellt.
Ein Leitfaden-Skill für Sonner, die Toast-Bibliothek. Wenn eine Aufgabe Sonner betrifft – Einrichtung, Rendern von Toasts, Styling oder Fehlerbehebung – antworte zuerst aus dieser Datei. Vollständige Prop-Tabellen für <Toaster /> und toast() befinden sich in API.md; lies sie, wenn du einen genauen Prop-Namen, -Typ oder -Standard benötigst.
Einrichtung
Zwei Teile, und nur zwei:
- Ein
<Toaster />, einmal eingebunden, so nah wie möglich am Root (in Next.js:layout.tsx– es funktioniert innerhalb von Server-Komponenten). Rendere es niemals pro Seite oder bedingt; ein zweiter eingebundener Toaster dupliziert jeden Toast. toast()wird aus Client-Code aufgerufen – Ereignis-Handler, Effekte, Callbacks. Es ist eine einfache Funktion, kein Hook oder Provider erforderlich, aber sie tut nichts auf dem Server: Gib in einer Server-Action das Ergebnis zurück und rufetoast()im Client-Code auf, der es empfängt.
import { Toaster } from 'sonner'; // einmal, im Layout
import { toast } from 'sonner'; // überall clientseitig
Den richtigen Aufruf wählen
| Gewünschtes Verhalten | Aufruf |
|---|---|
| Einfache Nachricht | toast('Titel') – füge { description } für eine zweite Zeile hinzu |
| Erfolgs-/Fehler-/Info-/Warn-Symbol | toast.success('…'), toast.error('…'), usw. |
| Spinner, während du den Zustand selbst verwaltest | toast.loading('…'), dann aktualisiere ihn per ID |
| Laden → Erfolg/Fehler, verknüpft mit einem Promise | toast.promise(promise, { loading, success, error }) – success/error akzeptieren Funktionen, die den aufgelösten Wert/Fehler erhalten |
| Schaltfläche, die etwas tut | { action: { label, onClick } } – schließt den Toast, es sei denn, onClick ruft event.preventDefault() auf; cancel ist die sekundäre Variante |
| Benutzerdefiniertes JSX, standardmäßige Toast-Hülle | toast(<jsx />) |
| Benutzerdefiniertes JSX, keine Stile | toast.custom((t) => <jsx />) – kopflos, t gibt die ID zum Schließen |
Rezepte
Einen Toast aktualisieren – rufe toast() erneut mit derselben id auf; nur die übergebenen Props ändern sich. Der Wechsel zu toast.success(…, { id }) ändert den Typ. So funktionieren Laden → Erfolg-Abläufe ohne toast.promise:
const id = toast.loading('Hochladen…');
toast.success('Hochgeladen', { id });
Beibehalten – { duration: Infinity }. Schließen – toast.dismiss(id), oder toast.dismiss() für alle. Aktive Toasts lesen – useSonner() in React, toast.getActiveToasts() außerhalb.
Links oder Komponenten im Text – übergib eine Funktion für den Titel oder die Beschreibung: toast(() => <a href="…">Anzeigen</a>).
Mehrere Toaster – gib jedem eine id und ziele mit toast('…', { toasterId: 'canvas' }). Ohne toasterId rendert jeder Toaster den Toast.
Schließen-Callbacks – onDismiss feuert beim Schließen-Button oder Wischen; onAutoClose feuert beim Timeout. Sie sind getrennt; es gibt keinen einzigen „geschlossen“-Callback.
Styling – die Eskalationsleiter
Steige nur so hoch, wie es die Änderung erfordert; zu früh auf die oberste Stufe zu springen ist in Ordnung (es ist der empfohlene Endzustand), in der Mitte zu verweilen nicht.
- Standardeinstellungen – plus
richColorsam Toaster für bunte Erfolgs-/Fehler-Toasts,invertzum Umdrehen gegen das Theme. - Inline-Anpassungen –
toastOptions={{ style: {…} }}am Toaster für alle Toasts, oderstyleprotoast()-Aufruf. - Klassen auf Teilen –
toastOptions={{ classNames: { toast, title, description, actionButton, cancelButton, closeButton } }}. Sonners eingefügte Styles gewinnen die Kaskade, daher benötigt jede Klasse!important(Tailwind:!text-red-900). Wenn du mehr als ein paar Dinge als wichtig markierst, höre auf – gehe kopflos. - Kopflos –
toast.custom()mit eigenem JSX, unter Beibehaltung von Sonners Positionierung, Stapelung und Wischfunktion. Der empfohlene Ansatz für einen Design-System-Toast: Wickle ihn in deine eigenetoast()-Abstraktion. (unstyled: trueexistiert als Zwischenlösung, aber kopflos bietet mehr Kontrolle bei gleichem Aufwand.)
Symbole – ersetze Standard-Symbole pro Typ mit der icons-Prop des Toasters, pro Toast mit icon, entferne sie mit null.
Theme – theme standardmäßig 'light' und folgt nicht dem Betriebssystem. Übergib theme="system" oder verbinde deinen Theme-Provider: <Toaster theme={resolvedTheme} /> von next-themes.
Fehlerbehebung
| Symptom | Ursache → Behebung |
|---|---|
| Toast erscheint nie | Kein <Toaster /> eingebunden oder es wurde ausgehängt (bedingtes Rendern, platzierung pro Seite). Binde einen am Root ein. Wenn der Aufruf aus einer Server-Action erfolgt: toast() ist nur clientseitig – rufe es mit dem Ergebnis der Action auf dem Client auf. |
| Gleicher Toast erscheint doppelt | Zwei Toaster eingebunden (Layout und Seite) – behalte einen. Oder toast() in einem Effekt unter React StrictModes dev-Doppelaufruf – rufe stattdessen vom Ereignis-Handler aus auf oder übergib eine stabile id, damit der zweite Aufruf aktualisiert statt dupliziert. |
| Tailwind/CSS-Klassen haben keine Wirkung | Standard-Styles überschreiben sie. Markiere sie mit !important oder verwende unstyled / kopflos (siehe die Leiter oben). |
| Toasts werden völlig ungestylt gerendert (häufig in Astro, View Transitions) | Sonners eingefügtes Stylesheet ging verloren – importiere es explizit in einem Layout: import 'sonner/dist/styles.css'. |
| Ungestylt innerhalb von Shadow DOM | Styles landen in document.head, nicht im Shadow Root. Kopiere das Style-Tag, dessen Text [data-sonner-toaster] enthält, in den Shadow Root. |
| Toast hinter einem Modal/Overlay oder abgeschnitten | Ein Vorfahre erzeugt einen Stacking-Kontext (transform, filter, overflow) oder das Overlay hat einen höheren z-Index als der Toaster. Verschiebe <Toaster /> an das Dokument-Root, außerhalb jedes Dialog-/Portal-Containers. |
| Dark Mode ignoriert | theme standardmäßig 'light' – setze theme="system" oder übergib das aufgelöste Theme (siehe Theme oben). |
| Erfolg/Fehler sehen grau aus, nicht grün/rot | Das ist die Voreinstellung. Füge richColors zum Toaster hinzu. |
| Toast schließt nie | duration: Infinity, dismissible: false, oder ein toast.promise, dessen Promise nie abgeschlossen wird – der Lade-Toast wartet für immer. |
toast.promise bleibt bei Ladezustand hängen | Es benötigt ein Promise (oder eine Funktion, die eines zurückgibt) als erstes Argument, und das Promise muss tatsächlich aufgelöst/abgelehnt werden. |
| Wischen zum Schließen geht falsch herum / funktioniert nicht | Richtungen leiten sich von position ab. Überschreibe mit swipeDirections am Toaster. |
| Toast erscheint in jedem Toaster | Mehrere Toaster benötigen gezielte Ansteuerung: gib jedem Toaster eine id und übergib toasterId im toast()-Aufruf. |
| Toasts zu nah am Bildschirmrand auf mobilen Geräten | offset (Desktop, Standard 32px) und mobileOffset (<600px, Standard 16px) – Zahlen, CSS-Strings oder seitenbezogene Objekte. |