# Toast (/ui/components/react/toast)



<Playground component="toast" />

## Usage [#usage]

```tsx
import { ToastProvider, Toaster, useToastManager } from '@appica/ui-react/toast'
```

Mount `ToastProvider` near the root of your app and render `Toaster` once inside it — that's the portal-anchored viewport every toast appears in. Then call `useToastManager().add(...)` from anywhere below the provider to fire one.

```tsx
function App() {
  return (
    <ToastProvider>
      <YourApp />
      <Toaster />
    </ToastProvider>
  )
}

function SaveButton() {
  const toast = useToastManager()
  return <Button onClick={() => toast.add({ title: 'Changes saved' })}>Save</Button>
}
```

A toast is **imperative**: you describe it as data (`title`, `description`, an optional icon, an action button) and the `Toaster` renders, stacks, and animates it for you. Each one auto-dismisses after `timeout` (5s by default), can be swiped away, and exposes a close button. `Toaster` accepts a `position` to pin the stack to any corner or edge.

<Callout>
  In the examples below, triggers fire real toasts that appear in their fixed corner of the page — scroll up if you
  don't see one land.
</Callout>

## Examples [#examples]

### Default [#default]

Fire a toast with a `title` and `description` from a `useToastManager().add(...)` call. It slides in, waits, and dismisses itself.

```tsx
'use client'

import { useToastManager } from '@appica/ui-react/toast'
import { Button } from '@appica/ui-react/button'
import { ToastSandbox } from './sandbox'

function Demo() {
  const toast = useToastManager()
  return (
    <Button
      onClick={() =>
        toast.add({
          title: 'Changes saved',
          description: 'Your preferences have been updated.',
        })
      }
    >
      Show toast
    </Button>
  )
}

export default function ToastDefault() {
  return (
    <ToastSandbox>
      <Demo />
    </ToastSandbox>
  )
}
```

### With an icon [#with-an-icon]

Pass a leading icon through the toast's typed `data` (`useToastManager<{ icon?: ReactNode }>()`). A filled, color-matched glyph signals success, error, or another state at a glance while the surface stays neutral.

```tsx
'use client'

import { useToastManager } from '@appica/ui-react/toast'
import { Button } from '@appica/ui-react/button'
import { CircleCheckFilled, CircleXFilled, AlertTriangleFilled, InfoCircleFilled } from '@appica/icons-react'
import { ToastSandbox } from './sandbox'

function Demo() {
  const toast = useToastManager<{ icon?: React.ReactNode }>()
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Payment received',
            description: 'Invoice #1024 has been settled.',
            data: { icon: <CircleCheckFilled className="text-success-emphasis" /> },
          })
        }
      >
        Success
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Upload failed',
            description: 'The file exceeds the 25 MB limit.',
            data: { icon: <CircleXFilled className="text-error-emphasis" /> },
          })
        }
      >
        Error
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Storage almost full',
            description: 'You have used 92% of your quota.',
            data: { icon: <AlertTriangleFilled className="text-warning-emphasis" /> },
          })
        }
      >
        Warning
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Heads up',
            description: 'Maintenance is scheduled for Sunday.',
            data: { icon: <InfoCircleFilled className="text-info-emphasis" /> },
          })
        }
      >
        Info
      </Button>
    </div>
  )
}

export default function ToastIconExample() {
  return (
    <ToastSandbox>
      <Demo />
    </ToastSandbox>
  )
}
```

### With a thumbnail [#with-a-thumbnail]

For more prominence, wrap the icon in a [`Thumbnail`](/ui/components/react/thumbnail) (`size="sm"`) with an `icon-*` variant — a tinted badge that pairs a soft background with the state color.

```tsx
'use client'

import { useToastManager } from '@appica/ui-react/toast'
import { Thumbnail } from '@appica/ui-react/thumbnail'
import { Button } from '@appica/ui-react/button'
import { Bell, CircleCheck, CircleX } from '@appica/icons-react'
import { ToastSandbox } from './sandbox'

function Demo() {
  const toast = useToastManager<{ icon?: React.ReactNode }>()
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'New notification',
            description: 'You have 3 unread messages.',
            data: {
              icon: (
                <Thumbnail variant="icon-soft" size="sm">
                  <Bell />
                </Thumbnail>
              ),
            },
          })
        }
      >
        Default
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Payment received',
            description: 'Invoice #1024 has been settled.',
            data: {
              icon: (
                <Thumbnail variant="icon-success" size="sm">
                  <CircleCheck />
                </Thumbnail>
              ),
            },
          })
        }
      >
        Success
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Upload failed',
            description: 'The file exceeds the 25 MB limit.',
            data: {
              icon: (
                <Thumbnail variant="icon-error" size="sm">
                  <CircleX />
                </Thumbnail>
              ),
            },
          })
        }
      >
        Error
      </Button>
    </div>
  )
}

export default function ToastThumbnailExample() {
  return (
    <ToastSandbox>
      <Demo />
    </ToastSandbox>
  )
}
```

### With an action [#with-an-action]

Add `actionProps` for an inline button — an "Undo", "Retry", or "View". Its `onClick` runs your handler; here it fires a follow-up toast.

```tsx
'use client'

import { useToastManager } from '@appica/ui-react/toast'
import { Button } from '@appica/ui-react/button'
import { ToastSandbox } from './sandbox'

function Demo() {
  const toast = useToastManager()
  return (
    <Button
      variant="outline"
      onClick={() =>
        toast.add({
          title: 'Message archived',
          actionProps: {
            children: 'Undo',
            onClick: () => toast.add({ title: 'Message restored' }),
          },
        })
      }
    >
      Archive message
    </Button>
  )
}

export default function ToastAction() {
  return (
    <ToastSandbox>
      <Demo />
    </ToastSandbox>
  )
}
```

### Promise [#promise]

Hand `toast.promise(...)` a promise plus `loading` / `success` / `error` states and it swaps the content as the promise settles — ideal for saves and uploads. (This demo fails at random to show both outcomes.)

```tsx
'use client'

import { useToastManager } from '@appica/ui-react/toast'
import { Thumbnail } from '@appica/ui-react/thumbnail'
import { Button } from '@appica/ui-react/button'
import { Spinner } from '@appica/ui-react/spinner'
import { CircleCheck, CircleX } from '@appica/icons-react'
import { ToastSandbox } from './sandbox'

function save() {
  return new Promise<{ name: string }>((resolve, reject) => {
    setTimeout(() => (Math.random() > 0.3 ? resolve({ name: 'report.pdf' }) : reject(new Error('Network error'))), 2000)
  })
}

function Demo() {
  const toast = useToastManager<{ icon?: React.ReactNode }>()
  return (
    <Button
      onClick={() =>
        toast.promise(save(), {
          loading: {
            title: 'Saving…',
            data: {
              icon: (
                <Thumbnail variant="icon-soft" size="sm">
                  <Spinner currentColor className="text-base" />
                </Thumbnail>
              ),
            },
          },
          success: {
            title: 'Saved',
            description: 'report.pdf is ready to share.',
            data: {
              icon: (
                <Thumbnail variant="icon-success" size="sm">
                  <CircleCheck />
                </Thumbnail>
              ),
            },
          },
          error: {
            title: 'Could not save',
            description: 'Check your connection and try again.',
            data: {
              icon: (
                <Thumbnail variant="icon-error" size="sm">
                  <CircleX />
                </Thumbnail>
              ),
            },
          },
        })
      }
    >
      Save document
    </Button>
  )
}

export default function ToastPromise() {
  return (
    <ToastSandbox>
      <Demo />
    </ToastSandbox>
  )
}
```

### Positions [#positions]

Set `position` on `Toaster` to anchor the stack: any of the six corners and edges. Toasts swipe toward their nearest edge to dismiss.

```tsx
'use client'

import * as React from 'react'
import { ToastProvider, Toaster, useToastManager, type ToastPosition } from '@appica/ui-react/toast'
import { Button } from '@appica/ui-react/button'

const POSITIONS: ToastPosition[] = [
  'top-left',
  'top-center',
  'top-right',
  'bottom-left',
  'bottom-center',
  'bottom-right',
]

function Picker({ onPosition }: { onPosition: (position: ToastPosition) => void }) {
  const toast = useToastManager()
  const show = (position: ToastPosition) => {
    onPosition(position)
    toast.add({ title: 'Notification', description: `Anchored ${position}.` })
  }
  return (
    <div className="grid grid-cols-3 gap-2">
      {POSITIONS.map((position) => (
        <Button key={position} variant="outline" size="sm" onClick={() => show(position)}>
          {position}
        </Button>
      ))}
    </div>
  )
}

export default function ToastPositions() {
  const [position, setPosition] = React.useState<ToastPosition>('bottom-right')
  return (
    <ToastProvider>
      <Picker onPosition={setPosition} />
      <Toaster position={position} />
    </ToastProvider>
  )
}
```

### Timeout & progress [#timeout--progress]

`timeout` controls how long a toast lingers — pass `timeout: 0` for a sticky toast that stays until dismissed. Progress is **opt-in**: set `progress` on the `Toaster` to draw a thin bar counting down on any auto-dismissing toast (including ones that use the default 5s). When you do, set `timeout` on the `Toaster` to match a custom `ToastProvider` timeout so the bar's duration lines up.

```tsx
'use client'

import { useToastManager } from '@appica/ui-react/toast'
import { Button } from '@appica/ui-react/button'
import { ToastSandbox } from './sandbox'

function Demo() {
  const toast = useToastManager()
  return (
    <div className="flex flex-wrap justify-center gap-2">
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Auto-dismiss',
            description: 'This toast closes in 6 seconds.',
            timeout: 6000,
          })
        }
      >
        Timed (6s)
      </Button>
      <Button
        variant="outline"
        onClick={() =>
          toast.add({
            title: 'Stays put',
            description: 'Set timeout to 0 to keep a toast until dismissed.',
            timeout: 0,
          })
        }
      >
        Persistent
      </Button>
    </div>
  )
}

export default function ToastTimeout() {
  return (
    <ToastSandbox progress>
      <Demo />
    </ToastSandbox>
  )
}
```

## RTL [#rtl]

Every Appica UI component supports right-to-left layouts out of the box. Set the `dir` attribute on a container (commonly your `<html>` element) so CSS logical properties resolve correctly, and wrap your tree in `DirectionProvider` so direction-aware behavior follows the same direction.

```tsx
import { DirectionProvider } from '@appica/ui-react/providers/direction-provider'

export default function RootLayout({ children }) {
  return (
    <html lang="ar" dir="rtl">
      <body>
        <DirectionProvider dir="rtl">{children}</DirectionProvider>
      </body>
    </html>
  )
}
```

Under RTL the toast's layout mirrors — the icon leads from the right, the close button moves to the left — and the countdown progress bar drains in the reading direction. For setup details and caveats, see the [RTL guide](/ui/docs/react/rtl).

<RtlPreview component="toast" />

## API reference [#api-reference]

Toast wraps [Base UI's Toast](https://base-ui.com/react/components/toast). Each part forwards its remaining props to the matching Base UI primitive and carries a `data-slot` for styling — the tables below list the Appica additions and the most common props.

### ToastProvider [#toastprovider]

Holds the toast queue and timing config. Wrap your app (or the subtree that fires toasts) in it. Renders no DOM of its own.

| Prop           | <ColMinWidth width="190">Type</ColMinWidth> | Default | <ColMinWidth width="230">Description</ColMinWidth>                         |
| -------------- | ------------------------------------------- | ------- | -------------------------------------------------------------------------- |
| `timeout`      | `number`                                    | `5000`  | Default auto-dismiss delay (ms) for toasts that don't set their own.       |
| `limit`        | `number`                                    | `3`     | Maximum toasts shown at once; the rest queue and surface as room frees up. |
| `toastManager` | `ToastManager`                              | —       | A manager from `createToastManager()` to drive toasts from outside React.  |
| `children`     | `ReactNode`                                 | —       | Your app subtree.                                                          |

### Toaster [#toaster]

The viewport: portals to the body and renders the live stack. Render exactly one per provider.

| Prop          | <ColMinWidth width="190">Type</ColMinWidth>                                                                  | Default          | <ColMinWidth width="230">Description</ColMinWidth>                                                                                         |
| ------------- | ------------------------------------------------------------------------------------------------------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `position`    | <Code>'top-left' \| 'top-center' \| 'top-right' \| 'bottom-left' \| 'bottom-center' \| 'bottom-right'</Code> | `'bottom-right'` | Where the stack is anchored; also sets the default swipe-to-dismiss directions.                                                            |
| `progress`    | `boolean`                                                                                                    | `false`          | Opt in to a countdown progress bar on auto-dismissing toasts.                                                                              |
| `timeout`     | `number`                                                                                                     | `5000`           | Provider default (ms) used to size the progress bar for toasts that don't set their own `timeout`. Match your `ToastProvider`'s `timeout`. |
| `container`   | <Code>HTMLElement \| Ref \| null</Code>                                                                      | `document.body`  | Portal target — scope the toasts to a specific element.                                                                                    |
| `portalProps` | <Code>Toast.Portal props</Code>                                                                              | —                | Escape hatch forwarded to the underlying `Toast.Portal`.                                                                                   |
| `className`   | <Code>string \| ((state) => string)</Code>                                                                   | —                | Extra classes on the viewport, merged via `tailwind-merge`.                                                                                |

`Toaster` reads the live `toasts` from the manager and renders each as a `Toast` with its title, description, optional `data.icon`, and `actionProps`.

### useToastManager [#usetoastmanager]

A hook returning the manager used to create and control toasts. Must be called under a `ToastProvider`.

| Method    | <ColMinWidth width="260">Signature</ColMinWidth> | Description                                                   |
| --------- | ------------------------------------------------ | ------------------------------------------------------------- |
| `add`     | <Code>(options) => string</Code>                 | Show a toast; returns its id.                                 |
| `update`  | <Code>(id, options) => void</Code>               | Patch an existing toast in place.                             |
| `close`   | <Code>(id) => void</Code>                        | Dismiss a toast by id.                                        |
| `promise` | <Code>(promise, states) => Promise</Code>        | Drive a toast through `loading` / `success` / `error` states. |
| `toasts`  | <Code>ToastObject\[]</Code>                      | The current live toasts (consumed by `Toaster`).              |

`createToastManager()` returns the same control surface (minus the reactive `toasts`) for firing toasts outside the React tree — pass it to `ToastProvider`'s `toastManager`.

#### `add` / `update` options [#add--update-options]

| Option        | <ColMinWidth width="170">Type</ColMinWidth> | Default | <ColMinWidth width="230">Description</ColMinWidth>                         |
| ------------- | ------------------------------------------- | ------- | -------------------------------------------------------------------------- |
| `title`       | `string`                                    | —       | The toast heading.                                                         |
| `description` | `string`                                    | —       | Supporting body text.                                                      |
| `timeout`     | `number`                                    | `5000`  | Auto-dismiss delay (ms). `0` keeps it until dismissed.                     |
| `type`        | `string`                                    | —       | A free-form category, also surfaced as `data-type` for styling.            |
| `priority`    | <Code>'low' \| 'high'</Code>                | `'low'` | `'high'` interrupts screen readers (use for errors).                       |
| `data`        | `T`                                         | —       | Your typed payload — the `Toaster` reads `data.icon` for the leading icon. |
| `actionProps` | <Code>button props</Code>                   | —       | Renders an inline action button (`children`, `onClick`, …).                |
| `onClose`     | <Code>() => void</Code>                     | —       | Fired when the toast is removed.                                           |

### Toast and its parts [#toast-and-its-parts]

`Toaster` assembles these for you, but they're exported for manual composition (custom layouts, extra parts): `Toast`, `ToastIcon`, `ToastTitle`, `ToastDescription`, `ToastActions`, `ToastAction`, `ToastClose`, `ToastProgress`, and the lower-level `ToastViewport` / `ToastPortal`.

| Prop               | Component              | <ColMinWidth width="150">Type</ColMinWidth> | Default          | Description                                                              |
| ------------------ | ---------------------- | ------------------------------------------- | ---------------- | ------------------------------------------------------------------------ |
| `toast`            | `Toast`                | `ToastObject`                               | —                | **Required.** The toast data from `toasts`.                              |
| `position`         | `Toast`                | `ToastPosition`                             | inherited        | Override the anchor/animation for a single toast.                        |
| `dismissible`      | `Toast`                | `boolean`                                   | `true`           | Render the close button.                                                 |
| `progress`         | `Toast`                | `boolean`                                   | `false`          | Show the countdown progress bar (when `timeout > 0`).                    |
| `swipeDirection`   | `Toast`                | <Code>axis \| axis\[]</Code>                | by position      | Allowed swipe-to-dismiss directions.                                     |
| `closeLabel`       | `Toast` / `ToastClose` | `string`                                    | `'Dismiss'`      | Accessible label for the close button.                                   |
| `variant` / `size` | `ToastAction`          | `buttonVariants`                            | `primary` / `sm` | Styles the action button like a [`Button`](/ui/components/react/button). |

## Accessibility [#accessibility]

* The `Toaster` viewport is a labelled landmark (an `aria-live` region); press **F6** to jump to it from anywhere on the page.
* New toasts are announced politely by default. Set `priority: 'high'` on important ones (errors, warnings) to interrupt and announce immediately.
* The close button carries an accessible label (`closeLabel`, default "Dismiss"); it's hidden from the a11y tree until the stack is hovered or focused, then revealed.
* Keep toast text short and self-contained — they disappear, so don't put the only copy of critical information in one.
* All enter/exit, stacking, and swipe animations honour `prefers-reduced-motion`, and the countdown progress bar is hidden under reduced motion.
