Skip to content
kerfjsGitHub

Toaster

Where toasts appear: short messages that come and go on their own.

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { Button, Toaster, toast } from '@kerf-js/veneer'export function Basic() {  return (  <>    <Button      onClick={() =>        toast.show({          title: 'Saved',          description: 'Your changes are live.',        })      }    >      Save changes    </Button>    <Toaster />  </>  )}

Installation

npm install @kerf-js/veneer

Try it

Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.

Live preview
Controls
Tone
Codeupdates as you change the controls
import { Toaster } from '@kerf-js/veneer'toast.show({ title, description, tone, action })<Toaster />

Usage

tsx
import { Toaster } from '@kerf-js/veneer'

Tones

Each tone brings its own icon.

tsx
import { Button, Toaster, toast } from '@kerf-js/veneer'export function Tones() {  return (  <>    {TONES.map((tone) => (      <Button        key={tone}        emphasis="outline"        tone={tone}        onClick={() =>          toast.show({ title: 'Deploy finished', tone })        }      >        {tone}      </Button>    ))}    <Toaster />  </>  )}const TONES = [  'neutral',  'positive',  'info',  'caution',  'critical',] as const

Position

Use position on Toaster to pick the corner.

tsx
import { state } from '@kerf-js/core'import {  Button,  Grid,  Toaster,  toast} from '@kerf-js/veneer'export function Position() {  const position =    state<(typeof CORNERS)[number]>('bottom-right')  const show = (corner: (typeof CORNERS)[number]) => {    position.value = corner    toast.show({      title: 'Deploy finished',      description: `Shown ${corner.replace('-', ' ')}.`,    })  }  return (    <>      <Grid        cols={3}        gap="tight"      >        {CORNERS.map((corner) => (          <Button            key={corner}            emphasis="outline"            tone="neutral"            onClick={() => show(corner)}          >            {corner}          </Button>        ))}      </Grid>      <Toaster position={position.value} />    </>  )}const CORNERS = [  'top-left',  'top-center',  'top-right',  'bottom-left',  'bottom-center',  'bottom-right',] as const

With an action

An action gives people ten seconds to take it.

tsx
import { state } from '@kerf-js/core'import { Button, Toaster, toast } from '@kerf-js/veneer'export function Undo() {  const archived = state(false)  const archive = () => {    archived.value = true    toast.show({      title: 'reports archived',      action: {        label: 'Undo',        onClick: () => (archived.value = false),      },    })  }  return (    <>      <Button        emphasis="outline"        tone="neutral"        disabled={archived.value}        onClick={archive}      >        {archived.value ? 'Archived' : 'Archive reports'}      </Button>      <Toaster />    </>  )}

Following a promise

toast.promise shows one toast from pending to done, in place.

tsx
import { Button, Toaster, toast } from '@kerf-js/veneer'export const Promise = () => {  const deploy = () =>    toast.promise(wait(2200), {      pending: {        title: 'Deploying storefront',        description: 'This takes about two seconds.',      },      done: {        title: 'storefront is live',        description: 'Serving 9f3ac21 in eu-west.',      },      error: 'The deploy failed',    })  return (    <>      <Button        icon="rocket"        onClick={deploy}      >        Deploy      </Button>      <Toaster />    </>  )}const wait = (ms: number) =>  new Promise((done) => {    setTimeout(done, ms)  })

Several at once

Three stack up, and fan out on hover. Hovering pauses every timer.

tsx
import { Button, Toaster, toast } from '@kerf-js/veneer'export const Several = () => {  const many = () =>    [      'eu-west',      'us-east',      'ap-south',      'eu-north',    ].forEach((region, i) =>      setTimeout(        () =>          toast.show({            title: `${region} is up to date`,            tone: 'positive',          }),        i * 250,      ),    )  return (    <>      <Button        emphasis="outline"        tone="neutral"        onClick={many}      >        Roll out to four regions      </Button>      <Toaster />    </>  )}

Closing from code

duration: 0 keeps it up until toast.dismiss with the id toast.show returned.

tsx
import { state } from '@kerf-js/core'import { Button, Toaster, toast } from '@kerf-js/veneer'export function Dismissed() {  const id = state('')  const start = () =>    (id.value = toast.show({      title: 'Watching for failures',      description: 'This stays until you stop it.',      duration: 0,    }))  const stop = () => {    toast.dismiss(id.value)    id.value = ''  }  return (    <>      <Button        emphasis="outline"        tone="neutral"        disabled={!!id.value}        onClick={start}      >        Start      </Button>      <Button        emphasis="outline"        tone="neutral"        disabled={!id.value}        onClick={stop}      >        Stop      </Button>      <Toaster />    </>  )}

On arrival

Fire one as a page mounts, to finish what the last page started.

tsx
import { onMount } from '@kerf-js/core'import { Text, Toaster, toast } from '@kerf-js/veneer'export function AfterRedirect() {  onMount(() =>    toast.show({      title: 'Welcome back, Mara',      description:        'You have two failed deploys to look at.',      tone: 'info',    }),  )  return (    <>      <Text subtle>        This one fires as the page arrives. Press Replay.      </Text>      <Toaster />    </>  )}

When to use

Use it for

  • Confirming something people just did
  • News that doesn't need an answer

Reach for something else

  • Alert, when it has to stay on the page until it is dealt with
  • Modal, when people must answer before going on

Accessibility

F8
Moves focus to the newest toast
Esc
Leaves the toasts
  • Toasts are read out through a live region that is on the page from the start.
  • Hovering or focusing the stack pauses every timer.
  • A critical toast with an action stays until it is dismissed.

API Reference

PropTypeDefaultDescription
position"top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center…—The corner or edge toasts stack in, such as top-center. Bottom right otherwise.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="toast"] [data-part="region"]

regionrooticontitledescriptionactionsprogress

RelatedAlertProgressButton