---
title: Toast
description: Show temporary status messages through solid-toaster integration.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# Toast

> Show temporary status messages through solid-toaster integration.

Use `solid-toaster` for temporary status messages. It is a separate package; Moraine does not
export a Toast component. Mount its host once and use Moraine icons and theme variables to match
your application.

## Basic usage

```tsx
import 'solid-toaster/style.css'

import { Button, Icon } from 'moraine'
import { BaseToaster, toast } from 'solid-toaster'

export default function App() {
  return (
    <>
      <Button onClick={() => toast.success('Changes saved!')}>Save</Button>
      <BaseToaster
        visibleToasts={4}
        preventDuplicate
        style={{
          '--normal-bg': 'var(--popover)',
          '--normal-text': 'var(--popover-foreground)',
          '--normal-border': 'var(--border)',
          '--border-radius': 'var(--radius)',
        }}
        icons={{
          success: () => <Icon name="icon-success" />,
          error: () => <Icon name="icon-error" />,
          warning: () => <Icon name="icon-warning" />,
          info: () => <Icon name="icon-info" />,
          loading: () => <Icon name="icon-loading" class="animate-spin" />,
          close: () => <Icon name="icon-close" />,
        }}
      />
    </>
  )
}
```

## Usage

### Install and mount the host

Install `solid-toaster`:

```shell title="bun" group-id="install-toaster"
bun add solid-toaster
```

```shell title="pnpm" group-id="install-toaster"
pnpm add solid-toaster
```

```shell title="npm" group-id="install-toaster"
npm i solid-toaster
```

Import `solid-toaster/style.css` and mount one `BaseToaster` at your application root before
calling `toast` methods. Its `icons` and `style` props connect the host to Moraine's semantic
icons and theme variables, as shown in Basic usage.

```tsx
import { Button, Icon } from 'moraine'
import { BaseToaster, toast } from 'solid-toaster'

export function SetupUsage() {
  const ICONS = {
    success: () => <Icon name="icon-success" />,
    error: () => <Icon name="icon-error" />,
    warning: () => <Icon name="icon-warning" />,
    info: () => <Icon name="icon-info" />,
    loading: () => <Icon name="icon-loading" class="animate-spin" />,
    close: () => <Icon name="icon-close" />,
  }

  const TOASTER_STYLE = {
    '--normal-bg': 'var(--popover)',
    '--normal-text': 'var(--popover-foreground)',
    '--normal-border': 'var(--border)',
    '--border-radius': 'var(--radius)',
  }

  return (
    <>
      <BaseToaster style={TOASTER_STYLE} visibleToasts={4} icons={ICONS} />
      <BaseToaster id="custom" position="bottom-left" style={TOASTER_STYLE} icons={ICONS} />
      <Button onClick={() => toast.success('Changes saved!')}>Trigger Success Toast</Button>
    </>
  )
}
```

### Calls and lifecycles

Use semantic toast calls for immediate feedback and `toast.promise` for a promise whose pending, success, and error states need distinct messages. Duration and scoped instances are supplied to the toaster integration.

```tsx
import { Button } from 'moraine'
import { toast } from 'solid-toaster'

export function CallsUsage() {
  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Button variant="outline" onClick={() => toast.success('Changes saved successfully!')}>
        Success
      </Button>
      <Button
        variant="outline"
        onClick={() => toast.error('Failed to update repository settings.')}
      >
        Error
      </Button>
      <Button variant="outline" onClick={() => toast.warning('Low storage remaining on volume.')}>
        Warning
      </Button>
    </div>
  )
}
```

### Duplicate messages

Set `preventDuplicate` on the host to avoid showing repeated toasts with the same content.

```tsx
import { Button, Icon } from 'moraine'
import { BaseToaster, toast } from 'solid-toaster'

export function PrevientDuplicate() {
  const ICONS = {
    success: () => <Icon name="icon-success" />,
    error: () => <Icon name="icon-error" />,
    warning: () => <Icon name="icon-warning" />,
    info: () => <Icon name="icon-info" />,
    loading: () => <Icon name="icon-loading" class="animate-spin" />,
    close: () => <Icon name="icon-close" />,
  }

  const TOASTER_STYLE = {
    '--normal-bg': 'var(--popover)',
    '--normal-text': 'var(--popover-foreground)',
    '--normal-border': 'var(--border)',
    '--border-radius': 'var(--radius)',
  }

  return (
    <div class="space-y-4">
      <BaseToaster
        position="top-right"
        preventDuplicate
        id="pd"
        icons={ICONS}
        style={TOASTER_STYLE}
      />
      <Button onClick={() => toast.success('Changes saved!', { toasterId: 'pd' })}>
        Trigger Success Toast
      </Button>
    </div>
  )
}
```

## Examples

### Basic toasts

```tsx
import 'solid-toaster/style.css'

import { Button } from 'moraine'
import { toast } from 'solid-toaster'

export function BasicToasts() {
  const wait = (ms: number) =>
    new Promise<void>((resolve) => {
      setTimeout(resolve, ms)
    })

  const runLoadingToast = async () => {
    const id = toast.loading('Uploading files...')
    await wait(1200)
    toast.success('Upload complete', { id })
  }

  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Button onClick={() => toast('Default message', { onAutoClose: console.log })}>
        Default
      </Button>
      <Button variant="secondary" onClick={() => toast.success('Saved successfully')}>
        Success
      </Button>
      <Button variant="outline" onClick={() => toast.warning('Careful with this action')}>
        Warning
      </Button>
      <Button variant="destructive" onClick={() => toast.error('Something went wrong')}>
        Error
      </Button>
      <Button variant="ghost" onClick={runLoadingToast}>
        Loading -&gt; Success
      </Button>
    </div>
  )
}
```

### Promise and scoped instances

```tsx
import { Button } from 'moraine'
import { createSignal } from 'solid-js'
import { toast } from 'solid-toaster'

export function PromiseScopedInstances() {
  const [promiseRuns, setPromiseRuns] = createSignal(0)
  const wait = (ms: number) =>
    new Promise<void>((resolve) => {
      setTimeout(resolve, ms)
    })

  const runPromiseToast = () => {
    const nextRun = promiseRuns() + 1
    setPromiseRuns(nextRun)
    const request = wait(1400).then(() => ({ run: nextRun }))

    toast.promise(request, {
      loading: `Sync #${nextRun} in progress...`,
      success: (result) => `Sync #${result.run} finished`,
      error: (error) => `Sync failed: ${String(error)}`,
      duration: 1e6,
    })
  }

  return (
    <div class="flex flex-wrap gap-3 items-center">
      <Button onClick={runPromiseToast}>Run promise toast ({promiseRuns()})</Button>
      <Button
        variant="outline"
        onClick={() => toast.info('To bottom-left custom', { toasterId: 'custom' })}
      >
        Send to custom toaster
      </Button>
      <Button variant="ghost" onClick={() => toast.dismiss()}>
        Dismiss all
      </Button>
    </div>
  )
}
```

