> For AI agents: the complete documentation index is available at https://wallarm.github.io/design-system/llms.txt, the full documentation bundle is available at https://wallarm.github.io/design-system/llms-full.txt.

# Toast

A transient notification fired from `useToast()` — you never place one in the tree, and a single `Toaster` at the app root owns placement, stacking and the timer. Because it disappears, nothing may live only here: reach for a `Dialog` when the reader has to decide, `Banner` for standing account state, and `Alert` for a message that belongs to a region. Actions on a toast stay optional for the same reason.

[Source](https://github.com/wallarm/design-system/tree/main/packages/design-system/src/components/Toast) · [Storybook](https://wallarm.github.io/design-system/storybook/?path=/docs/messaging-toast--overview)

```tsx
export const Basic = () => {
  return <ToastDemo />;
};
```

## Usage

```tsx
import { Toast } from '@wallarm-org/design-system/Toast';
```

## Examples

### With nested overlays

A toast fired from inside a `Drawer`, and again from a `Dialog` on top of it, to prove it renders above every open overlay rather than behind them.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Dialog, DialogBody, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@wallarm-org/design-system/Dialog';
import { Drawer, DrawerBody, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@wallarm-org/design-system/Drawer';
import { VStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const WithNestedOverlays = () => {
  const toast = useToast();

  const fireToast = () =>
    toast.create({
      title: 'Saved',
      description: 'This toast must render above every open overlay.',
      type: 'success',
      duration: STATIC_DURATION,
    });

  return (
    <Drawer data-testid='overlay-drawer'>
      <DrawerTrigger asChild>
        <Button variant='outline' color='neutral'>
          Open drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>[Level 1] Drawer</DrawerTitle>
        </DrawerHeader>

        <DrawerBody>
          <VStack gap={12} align='start'>
            <Button variant='outline' color='neutral' onClick={fireToast}>
              Show toast from drawer
            </Button>

            <Dialog>
              <DialogTrigger asChild>
                <Button variant='ghost' color='neutral'>
                  Open nested dialog
                </Button>
              </DialogTrigger>

              <DialogContent>
                <DialogHeader>
                  <DialogTitle>[Level 2] Dialog</DialogTitle>
                </DialogHeader>

                <DialogBody>
                  <Button variant='outline' color='neutral' onClick={fireToast}>
                    Show toast from nested dialog
                  </Button>
                </DialogBody>
              </DialogContent>
            </Dialog>
          </VStack>
        </DrawerBody>
      </DrawerContent>
    </Drawer>
  );
};
```

### Update loading to success

A loading toast has no timer and no progress bar, so it will not clear itself: `update()` the same id to resolve it to success or error.

```tsx
import { useRef } from 'react';
import { Button } from '@wallarm-org/design-system/Button';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const UpdateLoadingToSuccess = () => {
  const toast = useToast();
  const id = useRef<string | undefined>(undefined);
  const createToast = (duration?: number) => {
    id.current = toast.create({
      title: 'Loading',
      description: 'Loading ...',
      type: 'loading',
      variant: 'extended',
      duration,
    });
  };
  const updateToast = (duration?: number) => {
    if (!id.current) {
      return;
    }
    toast.update({
      id: id.current,
      title: 'Success',
      description: 'Success!',
      type: 'success',
      variant: 'extended',
      duration,
    });
  };
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => createToast()}>
            Create loading toast
          </Button>
          <Button variant='outline' color='neutral' onClick={() => updateToast()}>
            Update to success
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='secondary' color='neutral' onClick={() => createToast(STATIC_DURATION)}>
            Create loading toast
          </Button>
          <Button variant='secondary' color='neutral' onClick={() => updateToast(STATIC_DURATION)}>
            Update to success
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Description keeps extended

A `description` is never dropped: ask for `simple` alongside one and the toast still reflows to `extended`, timer included. Passing no description keeps the one-line layout.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const DescriptionKeepsExtended = () => {
  const toast = useToast();
  const withDescription = (duration?: number) =>
    toast.create({
      title: 'Failed to mark as True Positive',
      description: 'resolved vector count must not exceed 500000',
      type: 'error',
      variant: 'simple',
      duration,
    });
  const withoutDescription = (duration?: number) =>
    toast.create({ title: 'Marked 2 attacks', type: 'success', variant: 'simple', duration });
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => withDescription()}>
            Simple with description
          </Button>
          <Button variant='outline' color='neutral' onClick={() => withoutDescription()}>
            Simple without description
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button
            variant='secondary'
            color='neutral'
            onClick={() => withDescription(STATIC_DURATION)}
          >
            Simple with description
          </Button>
          <Button
            variant='secondary'
            color='neutral'
            onClick={() => withoutDescription(STATIC_DURATION)}
          >
            Simple without description
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Simple with actions

`simple` is a one-line title for about five seconds — as long as no `description` is passed with it. One action is the norm and two is the ceiling — they have to be optional, since the toast leaves on its own.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { ToastActions, useToast } from '@wallarm-org/design-system/Toast';

export const SimpleWithActions = () => {
  const toast = useToast();
  const single = (duration?: number) =>
    toast.create({
      title: 'Message sent',
      type: 'success',
      variant: 'simple',
      duration,
      actions: (
        <ToastActions>
          <Button variant='secondary' size='small' color='neutral-alt' onClick={onToastAction}>
            Undo
          </Button>
        </ToastActions>
      ),
    });
  const double = (duration?: number) =>
    toast.create({
      title: 'Connection restored',
      type: 'info',
      variant: 'simple',
      duration,
      actions: (
        <ToastActions>
          <Button variant='secondary' size='small' color='neutral-alt' onClick={onToastAction}>
            View
          </Button>
          <Button variant='secondary' size='small' color='neutral-alt' onClick={onToastAction}>
            Dismiss
          </Button>
        </ToastActions>
      ),
    });
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => single()}>
            Simple Toast with Action
          </Button>
          <Button variant='outline' color='neutral' onClick={() => double()}>
            Simple Toast with Two Actions
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='secondary' color='neutral' onClick={() => single(STATIC_DURATION)}>
            Simple Toast with Action
          </Button>
          <Button variant='secondary' color='neutral' onClick={() => double(STATIC_DURATION)}>
            Simple Toast with Two Actions
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Extended with actions

`extended` adds a description and about ten seconds, for the cases where one sentence of context genuinely helps.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { ToastActions, useToast } from '@wallarm-org/design-system/Toast';

export const ExtendedWithActions = () => {
  const toast = useToast();
  const single = (duration?: number) =>
    toast.create({
      title: 'Extended Toast',
      description: 'This is an extended toast with default layout.',
      type: 'success',
      variant: 'extended',
      duration,
      actions: (
        <ToastActions>
          <Button variant='secondary' size='small' color='neutral-alt' onClick={onToastAction}>
            Action
          </Button>
        </ToastActions>
      ),
    });
  const double = (duration?: number) =>
    toast.create({
      title: 'File processed',
      description: 'Your file has been processed successfully.',
      type: 'info',
      variant: 'extended',
      duration,
      actions: (
        <ToastActions>
          <Button variant='secondary' size='small' color='neutral-alt' onClick={onToastAction}>
            View
          </Button>
          <Button variant='secondary' size='small' color='neutral-alt' onClick={onToastAction}>
            Download
          </Button>
        </ToastActions>
      ),
    });
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => single()}>
            Extended Toast with Action
          </Button>
          <Button variant='outline' color='neutral' onClick={() => double()}>
            Extended Toast with Two Actions
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='secondary' color='neutral' onClick={() => single(STATIC_DURATION)}>
            Extended Toast with Action
          </Button>
          <Button variant='secondary' color='neutral' onClick={() => double(STATIC_DURATION)}>
            Extended Toast with Two Actions
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Long text

The title clamps to one line simple, two extended, and the description to four, with the full text in a tooltip. Pre-truncating your own copy fights it.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const LongText = () => {
  const toast = useToast();
  const longTitle =
    'This is a very very very long title that will be truncated after a certain number of lines';
  const veryLongTitle = longTitle.repeat(4);
  const longDescription =
    'This is a very long description that will be truncated after a certain number of lines. ' +
    'Hover over the description to see the full text in a tooltip. ' +
    'This allows for better space management while still providing access to all the information.'.repeat(
      2,
    );
  const simpleLong = (duration?: number) =>
    toast.create({ title: longTitle, variant: 'simple', type: 'info', duration });
  const extendedLong = (duration?: number) =>
    toast.create({
      title: veryLongTitle,
      variant: 'extended',
      description: longDescription,
      type: 'info',
      duration,
    });
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => simpleLong()}>
            Simple with long title
          </Button>
          <Button variant='outline' color='neutral' onClick={() => extendedLong()}>
            Extended with long text
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='secondary' color='neutral' onClick={() => simpleLong(STATIC_DURATION)}>
            Simple with long title
          </Button>
          <Button variant='secondary' color='neutral' onClick={() => extendedLong(STATIC_DURATION)}>
            Extended with long text
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Without close button

`closable: false` takes the X away, which is only right for an in-flight loading toast the reader must not lose.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const WithoutCloseButton = () => {
  const toast = useToast();
  const create = (duration?: number) =>
    toast.create({
      title: 'Non-closable toast',
      variant: 'extended',
      description: 'This toast cannot be closed manually.',
      type: 'info',
      closable: false,
      duration,
    });
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => create()}>
            Non-closable Toast
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='secondary' color='neutral' onClick={() => create(STATIC_DURATION)}>
            Non-closable Toast
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Custom icon

A custom `icon` replaces the type’s own. It inherits the type colour unless it sets one, so the first here is deliberately purple and the second is not.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Activity } from '@wallarm-org/design-system/icons';
import { HStack, VStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const CustomIcon = () => {
  const toast = useToast();
  const withColor = (duration?: number) =>
    toast.create({
      title: 'Custom icon toast',
      variant: 'extended',
      description: 'This toast uses a custom icon that overrides the type-based icon.',
      type: 'success',
      icon: <Activity size='lg' className='text-purple-500' />,
      duration,
    });
  const withoutColor = (duration?: number) =>
    toast.create({
      title: 'Custom icon with action',
      variant: 'extended',
      description: 'You can use any icon component as a custom icon.',
      type: 'info',
      icon: <Activity />,
      duration,
    });
  return (
    <VStack gap={16} align='start'>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='outline' color='neutral' onClick={() => withColor()}>
            Custom Icon with own color
          </Button>
          <Button variant='outline' color='neutral' onClick={() => withoutColor()}>
            Custom Icon without color
          </Button>
        </HStack>
      </VStack>
      <VStack gap={4} align='start'>
        <HStack gap={8} wrap='wrap'>
          <Button variant='secondary' color='neutral' onClick={() => withColor(STATIC_DURATION)}>
            Custom Icon with own color
          </Button>
          <Button variant='secondary' color='neutral' onClick={() => withoutColor(STATIC_DURATION)}>
            Custom Icon without color
          </Button>
        </HStack>
      </VStack>
    </VStack>
  );
};
```

### Custom icon colors

Neutral custom icons follow the toast's foreground. A status supplies its colour, while an explicit icon colour takes precedence.

```tsx
import { Button } from '@wallarm-org/design-system/Button';
import { Activity, Info } from '@wallarm-org/design-system/icons';
import { HStack } from '@wallarm-org/design-system/Stack';
import { useToast } from '@wallarm-org/design-system/Toast';

export const CustomIconColors = () => {
  const toast = useToast();
  const examples = [
    { name: 'default', label: 'Default', type: 'default' as const },
    { name: 'loading', label: 'Loading', type: 'loading' as const },
    { name: 'info', label: 'Info', type: 'info' as const },
    { name: 'own-color', label: 'Own color', type: 'success' as const },
  ];

  return (
    <HStack gap={8} wrap='wrap'>
      {examples.map(({ name, label, type }) => (
        <Button
          key={name}
          variant='outline'
          color='neutral'
          data-testid={`custom-icon-trigger-${name}`}
          onClick={() =>
            toast.create({
              id: `custom-icon-${name}`,
              title: `${label} custom icon`,
              type,
              duration: STATIC_DURATION,
              icon: (
                <Activity
                  data-testid={`custom-icon-${name}-svg`}
                  className={name === 'own-color' ? 'text-purple-500' : undefined}
                />
              ),
            })
          }
        >
          {label}
        </Button>
      ))}
    </HStack>
  );
};
```

## Props

| Prop     | Type        | Default | Description |
| -------- | ----------- | ------- | ----------- |
| toast \* | `ToastData` |         |             |
