# Toast

URL: https://ark-ui.com/docs/components/toast
LLM: https://ark-ui.com/llms.txt/components/toast

A message that appears on the screen to provide feedback on an action.

---



## Anatomy



```tsx
const toaster = createToaster({ placement: 'bottom-end' })

<Toaster toaster={toaster}>
  {(toast) => (
    <Toast.Root>
      <Toast.Title />
      <Toast.Description />
      <Toast.ActionTrigger />
      <Toast.CloseTrigger />
    </Toast.Root>
  )}
</Toaster>
```

## Setup

To use the Toast component, create the toast engine using the `createToaster` function.

This function manages the placement and grouping of toasts, and provides a `toast` object needed to create toast
notification.

```ts
const toaster = createToaster({
  placement: 'bottom-end',
  overlap: true,
  gap: 24,
})
```

## Examples

Here's an example of creating a toast using the `toast.create` method.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  placement: 'bottom-end',
  overlap: true,
  gap: 24,
})

export const Basic = () => {
  return (
    <div>
      <button
        type="button"
        className={button.Root}
        onClick={() =>
          toaster.create({
            title: 'Scheduled for tomorrow',
            description: 'Your meeting has been scheduled for tomorrow at 10am.',
            type: 'info',
          })
        }
      >
        Schedule meeting
      </button>
      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <Toast.Title className={styles.Title}>{toast.title}</Toast.Title>
              <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
              <Toast.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Toast.CloseTrigger>
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Types

You can create different types of toasts (`success`, `error`, `warning`, `info`) with appropriate styling. For example,
to create a success toast, you can do:

```ts
toaster.success({
  title: 'Success!',
  description: 'Your changes have been saved.',
})
```

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { CircleAlertIcon, TriangleAlertIcon, CircleCheckIcon, InfoIcon, XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  overlap: true,
  placement: 'bottom-end',
  gap: 16,
})

const iconMap = {
  success: CircleCheckIcon,
  error: CircleAlertIcon,
  warning: TriangleAlertIcon,
  info: InfoIcon,
}

export const Types = () => {
  return (
    <div>
      <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
        <button
          type="button"
          className={button.Root}
          onClick={() =>
            toaster.success({ title: 'Changes saved', description: 'Your profile has been updated successfully.' })
          }
        >
          Success
        </button>
        <button
          type="button"
          className={button.Root}
          onClick={() =>
            toaster.error({ title: 'Upload failed', description: 'There was an error uploading your file.' })
          }
        >
          Error
        </button>
        <button
          type="button"
          className={button.Root}
          onClick={() =>
            toaster.warning({ title: 'Low storage', description: 'You have less than 10% storage remaining.' })
          }
        >
          Warning
        </button>
        <button
          type="button"
          className={button.Root}
          onClick={() =>
            toaster.info({ title: 'Update available', description: 'A new version of the app is ready to install.' })
          }
        >
          Info
        </button>
      </div>

      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => {
            const ToastIcon = toast.type ? iconMap[toast.type as keyof typeof iconMap] : undefined
            return (
              <Toast.Root key={toast.id} className={styles.Root}>
                <Toast.Title className={styles.Title}>
                  {ToastIcon && <ToastIcon className={styles.Indicator} />}
                  {toast.title}
                </Toast.Title>
                <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
                <Toast.CloseTrigger className={styles.CloseTrigger}>
                  <XIcon />
                </Toast.CloseTrigger>
              </Toast.Root>
            )
          }}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Promise

You can use `toaster.promise()` to automatically handle the different states of an asynchronous operation. It provides
options for the `success`, `error`, and `loading` states of the promise and will automatically update the toast when the
promise resolves or rejects.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { XIcon, LoaderIcon, CircleCheckIcon, CircleAlertIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  overlap: true,
  placement: 'bottom-end',
  gap: 16,
})

const uploadFile = () => {
  return new Promise<void>((resolve, reject) => {
    setTimeout(() => {
      Math.random() > 0.5 ? resolve() : reject(new Error('Upload failed'))
    }, 2000)
  })
}

export const PromiseToast = () => {
  const handleUpload = async () => {
    toaster.promise(uploadFile, {
      loading: {
        title: 'Uploading file...',
        description: 'Please wait while we upload your document.',
      },
      success: {
        title: 'Upload complete',
        description: 'Your file has been uploaded successfully.',
      },
      error: {
        title: 'Upload failed',
        description: 'Could not upload the file. Please try again.',
      },
    })
  }

  const getIcon = (type: string | undefined) => {
    switch (type) {
      case 'loading':
        return <LoaderIcon className={styles.Indicator} data-type="loading" />
      case 'success':
        return <CircleCheckIcon className={styles.Indicator} />
      case 'error':
        return <CircleAlertIcon className={styles.Indicator} />
      default:
        return null
    }
  }

  return (
    <div>
      <button type="button" className={button.Root} onClick={handleUpload}>
        Upload file
      </button>

      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <Toast.Title className={styles.Title}>
                {getIcon(toast.type)}
                {toast.title}
              </Toast.Title>
              <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
              <Toast.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Toast.CloseTrigger>
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Update

To update a toast, use the `toast.update` method.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { useRef } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  placement: 'bottom-end',
  overlap: true,
  gap: 24,
})

export const Update = () => {
  const id = useRef<string>(undefined)

  const createToast = () => {
    id.current = toaster.create({
      title: 'Sending message...',
      description: 'Please wait while we deliver your message.',
      type: 'loading',
    })
  }

  const updateToast = () => {
    if (!id.current) {
      return
    }
    toaster.update(id.current, {
      title: 'Message sent',
      description: 'Your message has been delivered successfully.',
      type: 'success',
    })
  }

  return (
    <div className="hstack">
      <button type="button" className={button.Root} onClick={createToast}>
        Send message
      </button>
      <button type="button" className={button.Root} onClick={updateToast}>
        Mark as sent
      </button>
      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <Toast.Title className={styles.Title}>{toast.title}</Toast.Title>
              <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Action

To add an action to a toast, use the `toast.action` property.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  placement: 'bottom-end',
  gap: 24,
})

export const Action = () => {
  return (
    <div>
      <button
        type="button"
        className={button.Root}
        onClick={() =>
          toaster.create({
            title: 'Event has been created',
            description: 'We have sent you an email with the event details.',
            type: 'info',
            action: {
              label: 'Undo',
              onClick: () => {
                console.log('Undo')
              },
            },
          })
        }
      >
        Create event
      </button>
      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <Toast.Title className={styles.Title}>{toast.title}</Toast.Title>
              <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
              {toast.action && (
                <Toast.ActionTrigger className={styles.ActionTrigger}>{toast.action?.label}</Toast.ActionTrigger>
              )}
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Duration

You can control how long a toast stays visible by setting a custom `duration` in milliseconds, or use `Infinity` to keep
it visible until manually dismissed.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { XIcon, ClockIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  overlap: true,
  placement: 'bottom-end',
  gap: 16,
})

const durations = [
  { label: '1s', value: 1000 },
  { label: '3s', value: 3000 },
  { label: '5s', value: 5000 },
  { label: '∞', value: Infinity },
]

export const Duration = () => {
  return (
    <div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
        {durations.map((duration) => (
          <button
            key={duration.label}
            type="button"
            className={button.Root}
            onClick={() =>
              toaster.create({
                title: 'Reminder set',
                description: `This notification will ${
                  duration.value === Infinity ? 'stay until dismissed' : `disappear in ${duration.label}`
                }.`,
                type: 'info',
                duration: duration.value,
              })
            }
          >
            {duration.label}
          </button>
        ))}
      </div>

      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <Toast.Title className={styles.Title}>
                <ClockIcon className={styles.Indicator} />
                {toast.title}
              </Toast.Title>
              <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
              <Toast.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Toast.CloseTrigger>
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Max Visible

Set the `max` prop on the `createToaster` function to define the maximum number of toasts that can be rendered at any
one time. Any extra toasts will be queued and rendered when a toast has been dismissed.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { XIcon, InfoIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  max: 3,
  overlap: true,
  placement: 'bottom-end',
  gap: 16,
})

export const MaxToasts = () => {
  return (
    <div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px' }}>
        <button
          type="button"
          className={button.Root}
          onClick={() =>
            toaster.create({
              title: 'New notification',
              description: 'You have a new message in your inbox.',
              type: 'info',
            })
          }
        >
          Add notification
        </button>
        <button
          type="button"
          className={button.Root}
          onClick={() => {
            const messages = [
              'John liked your post',
              'Sarah commented on your photo',
              'New follower: @designpro',
              'Your post was shared 10 times',
              'Meeting reminder in 15 minutes',
            ]
            messages.forEach((msg) => {
              toaster.create({
                title: 'Notification',
                description: msg,
                type: 'info',
              })
            })
          }}
        >
          Add 5 notifications
        </button>
      </div>

      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <InfoIcon className={styles.Indicator} />
              <div style={{ flex: 1 }}>
                <Toast.Title className={styles.Title}>{toast.title}</Toast.Title>
                <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
              </div>
              <Toast.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Toast.CloseTrigger>
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

### Placement

Configure where toasts appear on the screen using the `placement` option in `createToaster`. Options include
`top-start`, `top-end`, `bottom-start`, `bottom-end`, and more.

```tsx
import { Portal } from '@ark-ui/react/portal'
import { Toast, Toaster, createToaster } from '@ark-ui/react/toast'
import { XIcon } from 'lucide-react'
import button from 'styles/button.module.css'
import styles from 'styles/toast.module.css'

const toaster = createToaster({
  placement: 'top-end',
  overlap: true,
  gap: 16,
})

export const Placement = () => {
  return (
    <div>
      <button
        type="button"
        className={button.Root}
        onClick={() =>
          toaster.create({
            title: 'Notification',
            description: 'This toast appears at the top-right corner.',
            type: 'info',
          })
        }
      >
        Show toast (top-end)
      </button>
      <Portal>
        <Toaster toaster={toaster}>
          {(toast) => (
            <Toast.Root key={toast.id} className={styles.Root}>
              <Toast.Title className={styles.Title}>{toast.title}</Toast.Title>
              <Toast.Description className={styles.Description}>{toast.description}</Toast.Description>
              <Toast.CloseTrigger className={styles.CloseTrigger}>
                <XIcon />
              </Toast.CloseTrigger>
            </Toast.Root>
          )}
        </Toaster>
      </Portal>
    </div>
  )
}
```

## Guides

### Toast in Effects

When creating a toast inside React effects (like `useEffect`, `useLayoutEffect`, or event handlers that trigger during
render), you may encounter a "flushSync" warning. To avoid this, wrap the toast call in `queueMicrotask`:

```tsx
import { useEffect } from 'react'

export const EffectToast = () => {
  useEffect(() => {
    // ❌ This may cause flushSync warnings
    // toaster.create({ title: 'Effect triggered!' })

    // ✅ Wrap in queueMicrotask to avoid warnings
    queueMicrotask(() => {
      toaster.create({
        title: 'Effect triggered!',
        description: 'This toast was called safely from an effect',
        type: 'success',
      })
    })
  }, [])

  return <div>Component content</div>
}
```

This ensures the toast creation is deferred until after the current execution context, preventing React's concurrent
rendering warnings.

### Styling

There's a minimal styling required for the toast to work correctly.

#### Toast root

The toast root will be assigned these css properties at runtime:

- `--x` - The x position
- `--y` - The y position
- `--scale` - The scale
- `--z-index` - The z-index
- `--height` - The height
- `--opacity` - The opacity
- `--gap` - The gap between toasts

```css
[data-scope='toast'][data-part='root'] {
  translate: var(--x) var(--y);
  scale: var(--scale);
  z-index: var(--z-index);
  height: var(--height);
  opacity: var(--opacity);
  will-change: translate, opacity, scale;
  transition:
    translate 400ms,
    scale 400ms,
    opacity 400ms,
    height 400ms,
    box-shadow 200ms;
  transition-timing-function: cubic-bezier(0.21, 1.02, 0.73, 1);

  &[data-state='closed'] {
    transition:
      translate 400ms,
      scale 400ms,
      opacity 200ms;
    transition-timing-function: cubic-bezier(0.06, 0.71, 0.55, 1);
  }
}
```

#### Styling based on type

You can also style based on the `data-type` attribute.

```css
[data-scope='toast'][data-part='root'] {
  &[data-type='error'] {
    background: red;
    color: white;
  }

  &[data-type='info'] {
    background: blue;
    color: white;
  }

  &[data-type='warning'] {
    background: orange;
  }

  &[data-type='success'] {
    background: green;
    color: white;
  }
}
```

#### Mobile considerations

A very common use case is to adjust the toast width on mobile so it spans the full width of the screen.

```css
@media (max-width: 640px) {
  [data-scope='toast'][data-part='group'] {
    width: 100%;
  }

  [data-scope='toast'][data-part='root'] {
    inset-inline: 0;
    width: calc(100% - var(--gap) * 2);
  }
}
```

## API Reference

### Props

### Root

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

#### Data Attributes

**`data-scope`**: toast
**`data-part`**: root
**`data-state`**: "open" | "closed"
**`data-type`**: The type of the item
**`data-placement`**: The placement of the toast
**`data-align`**: 
**`data-side`**: The side of the trigger that the toast is positioned on
**`data-mounted`**: Present when mounted
**`data-paused`**: Present when paused
**`data-first`**: 
**`data-sibling`**: 
**`data-stack`**: 
**`data-overlap`**: Present when overlapping

### ActionTrigger

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### CloseTrigger

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### Description

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### Store

#### Props

**`duration`**
Type: `number`
Required: false
Default Value: `undefined`
Description: The duration of the toast.
By default, it is determined by the type of the toast.

**`gap`**
Type: `number`
Required: false
Default Value: `16`
Description: The gap between the toasts

**`hotkey`**
Type: `string[]`
Required: false
Default Value: `'["altKey", "KeyT"]'`
Description: The hotkey that will move focus to the toast group

**`max`**
Type: `number`
Required: false
Default Value: `24`
Description: The maximum number of toasts. When the number of toasts exceeds this limit, the new toasts are queued.

**`offsets`**
Type: `string | Record<'top' | 'bottom' | 'left' | 'right', string>`
Required: false
Default Value: `"1rem"`
Description: The offset from the safe environment edge of the viewport

**`overlap`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Whether to overlap the toasts

**`pauseOnPageIdle`**
Type: `boolean`
Required: false
Default Value: `false`
Description: Whether to pause toast when the user leaves the browser tab

**`placement`**
Type: `Placement`
Required: false
Default Value: `"bottom"`
Description: The placement of the toast

**`removeDelay`**
Type: `number`
Required: false
Default Value: `200`
Description: The duration for the toast to kept alive before it is removed.
Useful for exit transitions.

### Title

#### Props

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

### Toaster

#### Props

**`toaster`**
Type: `CreateToasterReturn<any>`
Required: true
Default Value: `undefined`
Description: undefined

**`asChild`**
Type: `boolean`
Required: false
Default Value: `undefined`
Description: Use the provided child element as the default rendered element, combining their props and behavior.

**`dir`**
Type: `'ltr' | 'rtl'`
Required: false
Default Value: `"ltr"`
Description: The document's text/writing direction.

**`getRootNode`**
Type: `() => ShadowRoot | Node | Document`
Required: false
Default Value: `undefined`
Description: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `getCount` | `() => number` | The total number of toasts |
| `getToasts` | `() => ToastProps[]` | The toasts |
| `subscribe` | `(callback: (toasts: Options<O>[]) => void) => VoidFunction` | Subscribe to the toast group |
