pyreon

@pyreon/toast

Signal-driven toast notifications. Imperative API that works anywhere — no provider needed. Place <Toaster> once at your app root, then call toast() from any module.

Installation

npm install @pyreon/toast
bun add @pyreon/toast
pnpm add @pyreon/toast
yarn add @pyreon/toast

Peer dependencies: @pyreon/core, @pyreon/reactivity

Quick Start

import { toast, Toaster } from '@pyreon/toast'

function App() {
  return (
    <>
      <Toaster position="top-right" />
      <button onClick={() => toast.success('Saved!')}>Save</button>
    </>
  )
}
Toast Notifications

Imperative API — toast()

Show a toast from anywhere — components, stores, event handlers, async functions.

import { toast } from '@pyreon/toast'

// Basic toast (type: "info")
toast('Something happened')

// Typed shortcuts
toast.success('File uploaded')
toast.error('Connection failed')
toast.warning('Disk almost full')
toast.info('New version available')

// Loading toast (persistent, no auto-dismiss)
const id = toast.loading('Uploading...')

// Update an existing toast
toast.update(id, { message: 'Almost done...', type: 'info' })

// Dismiss
toast.dismiss(id)    // dismiss one
toast.dismiss()      // dismiss all

Every toast() call returns a unique string ID.

Toast Options

toast('Custom toast', {
  type: 'success',
  duration: 8000,
  dismissible: false,
  action: {
    label: 'Undo',
    onClick: () => undoAction(),
  },
  onDismiss: () => console.log('Gone'),
})
OptionTypeDefaultDescription
type'info' | 'success' | 'warning' | 'error''info'Toast variant — controls styling
durationnumber4000Auto-dismiss delay in ms. 0 = persistent
positionToastPositionfrom ToasterOverride position for this toast
dismissiblebooleantrueShow dismiss button
action{ label: string; onClick: () => void }Action button next to the message
onDismiss() => voidCalled when dismissed (manually or by timeout)

Promise Pattern

Auto-transitions a toast through loading → success/error states:

toast.promise(saveDocument(), {
  loading: 'Saving...',
  success: 'Document saved!',
  error: 'Failed to save',
})

// Dynamic messages from result/error
toast.promise(fetchUser(id), {
  loading: 'Loading user...',
  success: (user) => `Welcome, ${user.name}!`,
  error: (err) => `Error: ${err.message}`,
})

Toaster Component

Renders all active toasts via a <Portal>. Place once at your app root.

import { Toaster } from '@pyreon/toast'

function App() {
  return (
    <>
      <Toaster
        position="bottom-right"
        max={3}
        gap={12}
        offset={24}
      />
      <MyApp />
    </>
  )
}

Toaster Props

PropTypeDefaultDescription
positionToastPosition'top-right'Default position for all toasts
maxnumber5Maximum visible toasts
gapnumber8Gap between toasts in px
offsetnumber16Offset from viewport edge in px

Positions

'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'

Pause on Hover

Toast timers automatically pause when the mouse enters the toast container and resume when it leaves. No configuration needed.

Action Buttons

Add an action button to any toast for undo/retry patterns:

toast('Item deleted', {
  type: 'warning',
  duration: 6000,
  action: {
    label: 'Undo',
    onClick: () => restoreItem(id),
  },
})

Accessibility

Toast elements use role="alert" and aria-atomic="true". The container uses aria-live="polite" for screen reader announcements.

SSR

<Toaster> returns null on the server — safe to include in SSR layouts. toast() calls during SSR are safe (they queue into the signal, which is discarded).

TypeScript

import type {
  Toast,
  ToastOptions,
  ToastPosition,
  ToastType,
  ToasterProps,
  ToastPromiseOptions,
} from '@pyreon/toast'
Toast