Usage
Toaster where the app mounts its other providers, then call toast from anywhere.
AlertDialog.Alert or Banner.toast shows a neutral message. toast.info, toast.success, toast.warning, and toast.error set the severity, which picks the icon and its status color and how urgently a screen reader reads the message. toast(message, { severity }) takes the severity as data.
The icon, the text, and the buttons center on each other when the message is a single line with no description. A toast with a description, or a title that wraps to more than one line, hangs them from the top edge instead, so the icon stays beside the first line. Nothing is configured for this; the toast measures its own text.
An action is a Button in the toast. Pressing it runs onClick, then dismisses the toast without calling onDismiss. Keep the label to a word or two. A toast does not navigate: do the navigation in onClick.
Pass an id. A second call with the same id updates the toast in place and restarts its timer instead of stacking another.
A toast shows in the Toaster of the document that owns source, usually the button that was pressed (source: event.currentTarget). A message from a detached window stays in that window when that window has a Toaster; otherwise it goes to the first Toaster mounted.
While a modal dialog or modal drawer is open, the stack moves into it so it stays above the dialog and keeps taking input. It moves back when the dialog closes.
The title, description, action label, region label, and close label are all LocalizedMessage. The component supplies no text.
The shell owns the Toaster; a remote only raises toasts. @prepared911/ui/toast is one module the shell provides to every remote, and the registry of mounted Toasters lives on globalThis, so a remote's call reaches the shell's Toaster.
Toaster per document, at its root beside the app tree: in the root route's layout (the component around <Outlet />), outside every RemoteComponent slot and outside route content. The stack and its open toasts then survive navigation and a remote that unmounts. Its label and closeLabel come from the shell's formatMessage. The Toaster needs no provider, and its styles are in the @prepared911/ui/styles.css the shell entry already imports.Toaster in each pop-out window, gives it an id, and passes the same value as the toasterId of that pop-out's OverlayContainerProvider (see In a pop-out window).Toaster and never imports sonner. It raises toasts with useToast(), or with toast(message, { source: event.currentTarget }) from an event handler. A call from useToast() that names no toasterId or source goes to the toasterId of the nearest OverlayContainerProvider, else to the first Toaster mounted. A call with a source goes to the Toaster in that element's document.Toaster mounted. The call waits silently: nothing renders, nothing is logged, and nothing throws. It shows when a Toaster it can go to mounts, so a toast a remote raises from an effect on startup appears once the shell's Toaster registers. A shell that never mounts one never shows a toast, and the waiting calls stay in memory until toast.dismiss drops them. A call whose source is in a pop-out without a Toaster waits for one there and is never shown in the main window.Toaster in each window. A toast called with a source shows in the Toaster of the document that owns it, so a message from a button in the pop-out stays in the pop-out. When the content below an OverlayContainerProvider has no element to name, give the pop-out's Toaster an id, pass the same value as the provider's toasterId, and raise toasts with useToast().
When the panel docks and the pop-out's Toaster unmounts (or its window closes), the toasts open in it appear in the main window's Toaster with the same content, severity, action, callbacks and the time they had left. A toast pinned to the pop-out's Toaster with toasterId moves too; one pinned to another Toaster stays. With no other Toaster mounted, the toasts are dismissed.