Dots

Action Toast

The surface of a toast with actions, an undo countdown, progress or step segments.

$450.00 from Jordan LeeRent share · just now
Card frozen
"use client"
import {
  ActionToast,

The ActionToast is the surface of a toast that asks for a next step: money received with a view action, a frozen card with undo, a delayed send you can cancel, or a statement being prepared. It sits on the inverted primary fill and is built from parts, so you render it inside toast.custom() from the Sonner toaster or in any other live region. Use the plain toaster for short confirmations without actions, and ActionToast once the toast carries an action, a deadline or progress.

Installation

pnpm dlx shadcn@latest add @dots/action-toast

Usage

import {
  ActionToast,
  ActionToastAction,
  ActionToastActions,
  ActionToastContent,
  ActionToastDescription,
  ActionToastMedia,
  ActionToastTitle,
} from "@/components/ui/action-toast"
import { SnowflakeIcon } from "lucide-react"
import { toast } from "sonner"
toast.custom(
  (id) => (
    <ActionToast>
      <ActionToastMedia>
        <SnowflakeIcon />
      </ActionToastMedia>
      <ActionToastContent>
        <ActionToastTitle>Card frozen</ActionToastTitle>
      </ActionToastContent>
      <ActionToastActions>
        <ActionToastAction
          onPress={() => {
            unfreezeCard()
            toast.dismiss(id)
          }}
        >
          Undo
        </ActionToastAction>
      </ActionToastActions>
    </ActionToast>
  ),
  { unstyled: true }
)

Composition

Use the following composition to build an action toast:

ActionToast
├── ActionToastMedia or ActionToastCountdown
├── ActionToastContent
│   ├── ActionToastTitle
│   └── ActionToastDescription
├── ActionToastActions
│   └── ActionToastAction
└── ActionToastProgress or ActionToastSteps

Features

  • A pill for one line and a tile once there is a description, progress or a second row of actions
  • Media as an avatar, a bare icon, a round glyph or a spinner
  • One action at the end of the row, or two equal actions on their own row
  • An undo countdown ring that pauses while a pointer is over the toast or focus is inside it
  • Known progress on the kit Progress, and step segments for batches with failed steps
  • A destructive tone that fills only the glyph, never the whole toast

In a toaster

Render the toast with toast.custom() and pass unstyled: true, so ActionToast is the only surface. When a countdown owns the lifetime, set duration: Infinity and dismiss the toast in onComplete.

"use client"

import { toast } from "sonner"

Undo

Put an ActionToastCountdown in place of the media for an action that commits after a short window. Commit the action in onComplete, and change the countdown's key to start over. Say the deadline once in the description instead of announcing every second.

Monthly rent template
"use client"

import * as React from "react"

Delayed send

The same countdown works for a payment that leaves after a few seconds. Pass isPaused to stop the clock, for example while the payment is being reviewed, and say so in the description.

$100.00 to Alex Morgan
"use client"

import * as React from "react"

Progress

Add ActionToastProgress under the text for known progress, and set isPending on ActionToastMedia for work of unknown length.

Preparing statementJan – Oct · PDF
Transfer on the way$200.00 to Sam Price · checking
"use client"

import * as React from "react"

Steps

Use ActionToastSteps for an operation made of separate steps, such as a batch of payouts. Each step is "done", "failed" or "pending".

Sending payouts7 of 12 · $3,150.00 sent
"use client"
import {
  ActionToast,

Two actions

Two actions in ActionToastActions move to their own full-width row. Use emphasis="quiet" for the safer choice and keep one solid action.

"use client"
import {
  ActionToast,

Error

Set tone="destructive" on a glyph media for a failed payment or a security event, and say what failed in the description.

"use client"
import {
  ActionToast,

Accessibility

ActionToastAction renders the kit Button, built on React Aria, with press, focus and pending handling. ActionToastProgress and ActionToastSteps render a React Aria ProgressBar, so their values are read on demand, not announced. ActionToastSteps reads its value as "7 of 12" or "7 of 12, 2 failed".

Live region

ActionToast sets no role, because a toaster already announces what it renders. When you render it on its own, pass role="status", or role="alert" for errors and security events, or place it inside an element with that role.

<ActionToast role="alert">{/* ... */}</ActionToast>

Name progress

ActionToastProgress and ActionToastSteps have no visible label, so both require an aria-label or aria-labelledby.

Label icon-only actions

Give an ActionToastAction with size="icon" an aria-label, such as "Cancel statement".

Time to act

The countdown pauses while a pointer is over the toast or focus is inside it. It is hidden from assistive technology; put the deadline in the description, such as "undo within 5 seconds".

Media

ActionToastMedia is decorative by default because the title says what happened. Pass aria-hidden={false} when the media carries meaning of its own.

API Reference

ActionToast

The toast surface. Renders a <div>.

PropTypeDefaultDescription
shape"auto" | "pill" | "tile""auto"auto is a pill for one line and a tile once there is more. Force pill for an undo bar.
rolestring-Pass "status" or "alert" when the toast is not inside a toaster.
...propsReact.ComponentProps<"div">-Props spread to the root element.

ActionToastMedia

What the toast is about. Renders a <span>.

PropTypeDefaultDescription
variant"icon" | "glyph""icon"A bare icon or avatar, or the icon in a 40px circle.
tone"default" | "destructive""default"destructive fills only the glyph.
isPendingbooleanfalseShows a spinner instead of the children.
aria-hiddenbooleantrueSet to false when the media carries meaning.

ActionToastContent

Wraps the title and description.

PropTypeDefaultDescription
classNamestring-Additional classes to apply.

ActionToastTitle

What happened, such as "Template deleted".

PropTypeDefaultDescription
classNamestring-Additional classes to apply.

ActionToastDescription

The object or the detail. Its presence turns an auto toast into a tile.

PropTypeDefaultDescription
classNamestring-Additional classes to apply.

ActionToastActions

Holds one or two actions. Two actions move to their own full-width row.

PropTypeDefaultDescription
classNamestring-Additional classes to apply.

ActionToastAction

An action on the inverted surface. Renders a Button and accepts its props except variant and size.

PropTypeDefaultDescription
emphasis"solid" | "quiet""solid"solid for the main next step, quiet on a light fill.
size"default" | "icon""default"icon for an icon-only action; give it an aria-label.

ActionToastCountdown

A ring that empties over duration, with the whole seconds left in the middle.

PropTypeDefaultDescription
durationnumber-Required. Length of the window in seconds.
isPausedbooleanfalseStops the clock. Hover and focus inside the toast pause it too.
onComplete() => void-Called once when the clock reaches zero. Commit the action here.

ActionToastProgress

Known progress as a full-width bar. Renders the kit Progress and accepts its props.

PropTypeDefaultDescription
aria-label or aria-labelledbystring-Required. Names the progress.
valuenumber-The current progress.

ActionToastSteps

One segment per step. Renders a React Aria ProgressBar.

PropTypeDefaultDescription
stepsReadonlyArray<"done" | "failed" | "pending">-Required. One entry per step, in order.
aria-label or aria-labelledbystring-Required. Names the operation.
classNamestring-Additional classes to apply.