Notice Stack

    About this component

    Notice Stack

    A zero-dependency toast system: drop one `NoticeStack` in the layout and call `notice()` from anywhere, no provider needed. Notices stack like cards, the older ones tucked behind the newest, and fan out on hover or focus; they slide in and out on a soft overshoot, swipe away with a flick (the card resists when pulled toward the screen), pause their countdown while you read them or while the tab is hidden, and carry an optional action such as Undo. `notice.promise()` shows one notice that reads loading and turns into success or error in place. Six positions, success, error, warning, info and loading variants, polite or assertive announcements by variant, and F8 or Alt+T to jump into the stack.

    Usage

    The import and the props worth knowing about, in one place.

    import { NoticeStack, notice } from "@/components/beste/component/notice-stack";
    
    // Once, in the root layout
    <NoticeStack closeButton />
    
    // Anywhere after that
    notice("Setlist saved");
    notice.success("Tickets sent", { description: "Check hello@beste.co for the receipt." });
    notice.error("Payment declined", { duration: 8000 });
    
    notice("Track removed", {
      action: { label: "Undo", onClick: () => console.log("Restore the track") },
    });
    
    notice.promise(uploadMix(), {
      loading: "Uploading the mix",
      success: (file) => ({ title: "Mix uploaded", description: file.name }),
      error: "Upload failed",
    });
    
    const id = notice.loading("Syncing");
    notice.success("Synced", { id });   // same id: updates in place
    notice.dismiss();                   // clears every notice

    Keyboard and gestures

    Every one of these is in the component already. They are listed because a props table cannot mention a gesture, so nothing else on this page can tell you they exist.

    F8 / Alt + TMove the focus to the newest notice.
    TabWalk through the notices and their buttons; the stack fans out while it has focus.
    EscapeDismiss the focused notice and return the focus to where it was.
    HoverFan the stack out and pause every countdown.
    SwipeFlick a notice sideways or off the edge to dismiss it.

    Props

    Read from the component's own type, so this cannot drift from what it accepts.

    PropTypeDefaultDescription
    position"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right"—
    maxnumber—How many notices show at once; older ones wait behind.
    durationnumber—Default time on screen in milliseconds.
    expandboolean—Keep the stack fanned out instead of only on hover and focus.
    closeButtonboolean—A close button on every notice, shown on hover and focus.
    hotkeystring—Keys that move the focus to the notices.
    containedboolean—Pins the stack inside its parent instead of the window, for previews and embedded panels.
    offsetnumber—Distance from the edges, in pixels.
    tone"muted" | "outline" | "ghost"—
    size"sm" | "default" | "lg"—
    classNamestring—
    frameClassNamestring—Classes for the frame `contained` draws around the stack, e.g. a max width.
    childrenReact.ReactNode—Rendered before the stack; with `contained`, inside the same frame.