Skip to content

Toast / snackbar

In review
Live · web

Spawn a toast: they stack, newest on top, and auto-dismiss

0 live · action fired 0 times. Hover or tab a toast to pause its countdown; Esc dismisses it.
// Web: React, consuming @dizyx/nockerl-tokens. NockerlToast ships from
// @dizyx/nockerl-react: ONE transient floating card. The solid lifted surface,
// the leading filled status disc whose ring drains as the countdown, hover/focus
// pause, and Esc-to-dismiss are all in the package. NockerlToast is the single
// toast; the app owns the corner viewport + the stack (spawn / remove / cap).
import { useCallback, useRef, useState } from 'react';
import { NockerlToast, type NockerlToastProps } from '@dizyx/nockerl-react';
// The queued DATA (the toast fields minus the render-time callbacks) plus an id.
type ToastData = Omit<NockerlToastProps, 'onClose' | 'onAction'>;
interface Queued extends ToastData {
id: number;
}
function useToasts() {
const [toasts, setToasts] = useState<Queued[]>([]);
const idRef = useRef(0);
const remove = useCallback((id: number) => setToasts((l) => l.filter((t) => t.id !== id)), []);
const show = useCallback((t: ToastData) => {
const id = ++idRef.current;
setToasts((l) => [...l, { ...t, id }].slice(-4)); // cap the visible stack at 4
}, []);
return { toasts, show, remove };
}
function SessionActions() {
const { toasts, show, remove } = useToasts();
return (
<>
<button onClick={() => show({ intent: 'info', message: 'A new session opened on api-server.', duration: 5000 })}>
Open
</button>
<button
onClick={() =>
show({
intent: 'error',
title: 'Message failed to send',
message: 'The gateway returned a 502.',
actionLabel: 'Undo',
duration: 0, // 0 → persistent: stays until dismissed
})
}
>
Send
</button>
{/* Mount the stack in a corner viewport (bottom-right here); NockerlToast is one card. */}
<div className="toast-viewport">
{toasts.map((t) => (
<NockerlToast key={t.id} {...t} onClose={() => remove(t.id)} onAction={resend} />
))}
</div>
</>
);
}

NockerlToast renders ONE toast from a NockerlToastProps object plus an onClose (timer / Esc / close-X) and an optional onAction (the action button). The corner viewport + the stack (spawn / remove / cap) are the app’s. Mount the cards where you want them; the toast owns its own countdown, hover/focus pause, and Esc dismissal.

PropTypeDefaultDescription
message *stringBody copy that carries the toast's accessible text (rendered as HTML).
onClose *() => voidDismiss handler, fired when the timer elapses, Esc is pressed, or the close (X) is clicked.
intentNockerlToastIntent'info'Drives the status color, the default icon, and the live role.
titlestringOptional bold heading above the message (one short line).
iconbooleantrueShow the leading status disc (defaults to on).
actionLabelstringOptional quiet text button in the status color (right-aligned; e.g. Undo).
onAction() => voidAction handler (ignored when no actionLabel).
durationnumberAuto-dismiss after this many ms. 0 / undefined → PERSISTENT (no timeout); the toast then shows a "pinned" marker instead of the ring and waits for close.