@rozie-ui/toast-react
v0.1.9
Published
Idiomatic React headless, accessible toast / notification host (queue, auto-dismiss, hover-pause, positioning, imperative show/dismiss/clear handle) — one accessible Rozie source compiled to React.
Maintainers
Readme
@rozie-ui/toast-react
Idiomatic react Toaster — a headless, accessible toast / notification host (a live-region queue with per-toast auto-dismiss timers, hover-to-pause, six corner positions, and a per-toast close button) compiled from one Rozie source. It is not a global singleton + context system: the host owns the queue + timers as internal state and exposes an imperative show / dismiss / clear handle you drive via ref — "call from anywhere" is your app's wiring concern (stash the ref). Every visual value is a CSS custom property, so it re-skins to any design system. This package is generated; do not edit src/ by hand.
Install
npm i @rozie-ui/toast-reactPeer dependencies: react + react-dom. Install them alongside this package.
Also installed: @rozie/runtime-react — Rozie's small, tree-shaken runtime helper package (controllable state, keyboard navigation, event modifiers, and safe interpolation). It arrives as a regular dependency, so npm pulls it for you. Your bundler keeps only the helpers this component actually uses — typically a few hundred bytes to a few KB, minified and gzipped. What's in it and what it costs.
Usage
import { useRef } from 'react';
import { Toaster, type ToasterHandle } from '@rozie-ui/toast-react';
export function Demo() {
const toaster = useRef<ToasterHandle>(null);
return (
<>
<button onClick={() => toaster.current?.show({ message: 'Saved!', type: 'success' })}>
Save
</button>
<button onClick={() => toaster.current?.show({ message: 'Something failed', type: 'error' })}>
Fail
</button>
{/* Mount the host once (typically near the app root). */}
<Toaster ref={toaster} position="bottom-right" duration={4000} />
</>
);
}
// Custom per-toast chrome via the #toast scoped slot:
// <Toaster ref={toaster}>
// {({ toast, dismiss }) => (
// <div className="my-toast">
// <strong>{toast.type}</strong> {toast.message}
// <button onClick={() => dismiss(toast.id)}>OK</button>
// </div>
// )}
// </Toaster>Theming
Every visual value is a --rozie-toast-* CSS custom property — override any of them at any ancestor scope. Ready-made design-system bridges ship in the package:
import '@rozie-ui/toast-react/themes/shadcn.css'; // or material.css, bootstrap.css, base.cssProps
| Name | Type | Default | Two-way (model) | Required |
| --- | --- | --- | :---: | :---: |
| position | String | "bottom-right" | | |
| duration | Number | 4000 | | |
| max | Number | 0 | | |
| disablePauseOnHover | Boolean | false | | |
| ariaLabel | String | null | | |
| disableSwipe | Boolean | false | | |
| stacked | Boolean | false | | |
Events
| Event | Description |
| --- | --- |
| dismissed | Fired exactly once per toast, at dismissal initiation (before the exit animation runs). Payload is ONE object { toast, reason } — toast is the full queue entry, reason is 'timeout' (auto-dismiss), 'swipe' (pointer swipe past threshold), 'close' (the built-in close button), or 'api' (the dismiss(id) verb). clear() removes every toast immediately and does NOT fire dismissed (documented bulk behavior). |
Imperative handle
The component has no events — its primary API is an imperative handle (declared once in the Rozie source via $expose). Grab a handle with the native ref mechanism and call the methods directly. None of the verbs overrides an inherited host-element member, so the Lit custom element emits no ROZ137 warning:
| Method | Description |
| --- | --- |
| show | Enqueue a toast. Accepts { message, type, duration, id } (all optional — message defaults to '', type to 'info', duration to the duration prop). Returns the toast id. A non-sticky toast (duration > 0) auto-dismisses; duration: 0 makes it sticky. |
| dismiss | Remove a single toast by the id returned from show (routes through the exit lifecycle with reason 'api' — fires dismissed, plays the exit animation, then removes it). |
| clear | Remove every visible toast at once immediately (no exit animation) and clear all pending auto-dismiss timers. Does NOT fire dismissed. |
| patch | Update an existing toast in place. Accepts (id, { message, type, duration }) — only the keys you pass are merged into the matching entry. Returns true if the id existed, false otherwise (no throw). Including a duration key clears and restarts that toast's auto-dismiss timer (0 makes it sticky; a positive value arms/re-arms it); omitting duration leaves a running timer untouched. |
| promise | Sugar over show/patch for an async operation: promise(p, { loading, success, error }) immediately shows a { type: 'loading', duration: 0 } toast and returns its id SYNCHRONOUSLY. On resolve it patches the SAME toast to { type: 'success', message: resolve(success, value) } (the auto-dismiss timer starts AT SETTLE); on reject, likewise with error. success/error accept a string or a (value) => string function. Never resurrects a toast dismissed while p was still pending, and never returns a derived promise — your own .then/.catch on p still fire. |
import { useRef } from 'react';
import { Toaster, type ToasterHandle } from '@rozie-ui/toast-react';
const toaster = useRef<ToasterHandle>(null);
// <Toaster ref={toaster} ... />
const id = toaster.current?.show({ message: 'Saved', type: 'success' });
toaster.current?.dismiss(id!);
toaster.current?.clear();Slots
| Slot | Params | | --- | --- | | toast | toast, dismiss |
