Skip to content
  1. Keyline UI
  2. Toast
Keyline UIComponents
  • Get started
    • Installation
    • Theming
    • Modes
    • Configuration
    • Composition
  • Actions
    • Button
    • Button group
    • Split button
    • Toggle
    • Toggle group
    • Toolbar
    • Menu
    • Menubar
    • Context menu
  • Inputs
    • Field and form
    • Checkbox
    • Checkbox group
    • Radio group
    • Switch
    • Slider
    • Segmented
    • Select
    • Native select
    • Combobox
    • Multiselect
    • Number field
    • OTP field
    • Time field
    • Date picker and calendar
    • Date range
  • Data
    • Table
    • Chart
    • Stat and meter
    • Status and badge
    • Progress
    • Skeleton
    • Empty
    • Avatar
    • Kbd
    • Marker
  • Navigation
    • Tabs
    • Breadcrumb
    • Pagination
    • Nav
    • Navigation menu
    • Command menu
  • Overlays
    • Dialog
    • Alert dialog
    • Drawer
    • Sheet
    • Popover
    • Hover card
    • Tooltip
    • Toast
    • Notice
  • Layout
    • App shell
    • Sidebar
    • Panel
    • Resizable
    • Scroll area
    • Accordion
    • Collapsible
    • Carousel
    • Inspector
    • Item
    • Direction
  • Conversation
    • Message
    • Bubble
    • Message scroller
    • Attachment
    • Questionnaire
  • Blocks
    • Shipments

Toast

A short record of something that just happened, with at most one action. The newest sits in front and counts the ones waiting behind; hover or focus fans them out and pauses every timer. The bottom keyline drains as the time runs out. Swipe it away, or press its close cell.

Loading the live example…

"use client"

import { useState } from "react"
import { Button } from "@/components/keyline/ui/button"
import { ToastFloating, ToastProvider, useToaster } from "@/components/keyline/ui/toast"

const ORDERS = ["DM-4103 · Scranton Business Park", "DM-4104 · Penn Paper Supply", "DM-4105 · Blue Cross of Pennsylvania"]

function Orders() {
  const toast = useToaster()
  const [orders, setOrders] = useState(ORDERS)

  const archive = (o: string) => {
    const at = orders.indexOf(o)
    setOrders((all) => all.filter((x) => x !== o))
    toast.notify({
      title: `${o.split(" · ")[0]} archived`,
      description: "It stays in the quarter's reports.",
      action: { label: "Undo", onClick: () => setOrders((all) => [...all.slice(0, at), o, ...all.slice(at)]) },
    })
  }

  return (
    <ul aria-label="Delivered orders" className="m-0 grid list-none gap-3 p-0 text-[14px]">
      {orders.map((o) => (
        <li key={o} className="grid grid-cols-[1fr_auto] items-center gap-3">
          <span>{o}</span>
          <Button size="compact" onClick={() => archive(o)}>
            Archive
          </Button>
        </li>
      ))}
      {orders.length ? null : <li className="text-kl-ink-2">Everything delivered is archived.</li>}
    </ul>
  )
}

export function ArchiveUndo() {
  return (
    <ToastProvider>
      <Orders />
      <ToastFloating />
    </ToastProvider>
  )
}

Installation

npx shadcn@latest add @keyline-ui/toast

Run it where your components.json is. The first time, map the @keyline-ui registry: one command, in Installation.

Usage

import { ToastFloating, ToastProvider, useToaster } from "@/components/keyline/ui/toast"
<ToastProvider>
  <App />
  <ToastFloating />
</ToastProvider>

const toast = useToaster()
toast.notify({ title: "DM-4103 archived", action: { label: "Undo", onClick: restore } })

Examples

Track a promise

promise shows one toast while the work runs and turns it into done or failed in place. The first send here fails so you can see the retry.

Loading the live example…

"use client"

import { useRef } from "react"
import { Button } from "@/components/keyline/ui/button"
import { ToastFloating, ToastProvider, useToaster } from "@/components/keyline/ui/toast"

function Send() {
  const toast = useToaster()
  const tries = useRef(0)

  const send = () => {
    tries.current += 1
    // the first send fails (corporate's fax is jammed), the retry gets through
    const fails = tries.current === 1
    const sent = new Promise<number>((ok, no) => setTimeout(() => (fails ? no(new Error("busy")) : ok(14)), 1600))
    toast
      .promise(sent, {
        loading: { title: "Sending the quarterly report to corporate…" },
        done: (pages) => ({ title: "Report sent to Jan", description: `${pages} pages, received in New York.` }),
        failed: { title: "Corporate didn't pick up", description: "Their fax line is busy.", action: { label: "Retry", onClick: send } },
      })
      .catch(() => {})
  }

  return <Button onClick={send}>Send to corporate</Button>
}

export function SendReport() {
  return (
    <ToastProvider>
      <Send />
      <ToastFloating />
    </ToastProvider>
  )
}

Props

PropTypeDefaultDescription
notify({ title, description?, type?, action?, timeout? }) => idDefault —From useToaster(). type is done, failed, info, warning or loading.
promise(p, { loading, done, failed }) => pDefault —One toast that follows a promise. Each state takes a title, a full toast, or a function of the result.
update · dismiss(id, …) · (id?)Default —Change a toast in place, or close one or all.
timeout · limitnumberDefault 6000 · 3On ToastProvider: how long a toast stays, and how many show at once.
ToastDockViewportDefault —Shows toasts inside a cell of your layout, such as a table&apos;s footer, so they never cover data.
createToaster() => managerDefault —A toaster to call from outside React, such as a data layer.

For a condition that is still true, such as low stock, use Notice. Built on Base UI Toast.

Sheet 55 of 73PreviousTooltipNextNotice
On this page