BetaFeedback

Toast

A notice that arrives because something happened, and leaves on its own.

Overview

A notice that arrives because something happened, and leaves on its own.

First example

<XAUIProvider>
<ToastHost placement="bottom" duration={4000} maxVisible={3}>
{app}
</ToastHost>
</XAUIProvider>

Anatomy

;<ToastHost>{app}</ToastHost>

// anywhere below it
const { toast } = useToast()

toast({
  render: () => (
    <Toast variant="success">
      <Toast.Title>Enregistré</Toast.Title>
      <Toast.Description>Vos modifications sont sur le serveur.</Toast.Description>
      <Toast.Actions>
        <Toast.Close asChild>
          <Button size="sm">Fermer</Button>
        </Toast.Close>
      </Toast.Actions>
    </Toast>
  ),
})
  • ToastHost — the queue, the timers and the stack. Mounted once.
  • useToast — how anything asks for one.
  • Toast — the card. Presentational, and replaceable.
  • Toast.Title — what happened. The one node the variant colours.
  • Toast.Description — the detail.
  • Toast.Actions — the row of things you can do about it.
  • Toast.Close — anything that sends this one away early.

Usage

The host

<XAUIProvider>
  <ToastHost placement="bottom" duration={4000} maxVisible={3}>
    {app}
  </ToastHost>
</XAUIProvider>

It renders into the nearest PortalHost, so the stack sits over navigation rather than inside whatever screen asked for it.

One that waits for an answer

toast({ duration: 0, render: … })

0 keeps it until something dismisses it — for the toast that asks a question rather than reporting an answer.

Dismissing early

const id = toast({ render: … })
dismiss(id)

render is also handed { dismiss }, which is the same thing without holding the id.

Root props

ToastProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo
variantToastVariant | undefinedNo
radiusRadiusKey | undefinedNo

Slots

ToastActionsProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo

ToastCloseProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo
asChildboolean | undefinedNo

ToastDescriptionProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo

ToastTitleProps

The node's inherited React Native props apply too, and so do its style props padding, margin, width and the rest.

PropTypeRequiredDescription
childrenReactNodeNo

Variants, sizes and colour

The variant paints the title, and nothing else

A red card sliding in from the edge of the screen reads as the app breaking; a red line of text reads as the thing you just did failing. The surface stays the theme's floating one whatever happened — which is also what lets two toasts of different kinds stack without the pile looking like a paint chart.

It uses the soft foregrounds rather than the full colours: a toast is read at a glance and from the corner of the eye, and danger at full strength on an overlay surface is a shout where the soft one is a statement.

The description stays muted whatever the variant. The title already said in colour what kind of thing happened, and saying it twice leaves nothing for the eye to rank.

Accessibility

The card is an alert with accessibilityLiveRegion="polite", so it is announced when it arrives rather than when the reader reaches it — a toast that waits its turn in the reading order has usually gone by the time it is read.

useToast outside a host warns and does nothing rather than throwing. A missing host is a setup mistake in the app shell, and a screen that crashes on its way to reporting that a save succeeded has turned a good outcome into a bad one.

Migration from legacy

This component declares no rule of its own. The migration guide covers variants, colours and slots.

Implementation notes

The split, and why it is the whole design

Toast does not know it is in a queue, when it will leave, or what is stacked under it. The host owns all three.

That is what lets render return anything at all — the queue never looks at it. Toast is the card this library ships, not the card the host requires.

Toast.Close still knows which toast it belongs to without being told: the host provides the dismiss around each entry, and the card folds it into the context its slots read. A close button two levels down needs nothing passed to it.

The stack

The cards are not laid out one under another. Every one is anchored to the same edge, and its depth is entirely in its transform, so a pile of eight costs the height of one:

depthtranslateYscale
0 (front)01
110 toward the edge0.97
2200.94

Both numbers are the reference implementation's, read off their toast.animation.ts: translateY: [0, 10] and scale: [1, 0.97], interpolated over the index. The shoulder a card leaves is 10 − 0.03 × height, around 7 points on a two-line toast — enough to say there is another one, not enough to be read as a second card.

The ladder does not stop. Their interpolation clamps the front side only, so the fourth card is genuinely further back than the third rather than sitting exactly on it and reading as one. What ends the ladder is maxVisible, past which the card is transparent.

Newest is painted last, so it lands in front without a zIndex. A card past maxVisible takes no touches: a transparent card is still a target, and a press meant for the front one must not land on something nobody can see.

The swipe

The front card is thrown away by dragging it away from its edge — up on a top stack, down on a bottom one. The pile empties one card at a time, each swipe promoting the next.

It goes past 50 points or 500 points a second, the reference implementation's thresholds, and either alone is enough: distance without velocity refuses a flick that clearly meant it, velocity without distance refuses a slow deliberate push. A toast is glanced at, so both readings count.

Dragged the wrong way it resists rather than refuses — the whole screen's travel maps onto 40 points, so the card answers the finger without pretending it can go there. Under a finger it sinks half a percent, which is meant to be felt rather than seen.

The throw carries on at the speed the finger left it (withDecay, velocity × 1.5) and the record goes a moment later, so a hard flick leaves faster than a soft one and neither is cut off at the frame the finger lifted.

Only the card in front. The ones behind show a seven-point shoulder — a target under any reasonable minimum, and dragging the second card out from under the first reads as a glitch rather than as a dismissal.

The gesture runs on react-native-gesture-handler, an optional peer of this package. It is reached only through @xaui/native/toast, so a project that never imports a toast never loads it. Set isSwipeable={false} to leave the pile alone.

Toast

proptypedefaultdescription
variantToastVariantdefaultColours the title only
radiusRadiusKeyOverrides the card's corner

default · accent · success · warning · danger.

Motion

It slides from the edge it will sit against, and leaves the same way — 260 ms in, 180 ms out.

That is what separates it from every other overlay here. A dialog and a popover appear where they are, because they were asked for; a toast arrives, because something happened. Motion across the screen's edge is the difference between the two.

It replaces `Snackbar`

The legacy component is Snackbar. It is the same object under two names, and the reference implementation calls it toast; the roadmap's P5.18 closes with this.

Legacyv1
<Snackbar message="…" /><Toast.Title>…</Toast.Title>
action={{ label, onPress }}a Button inside <Toast.Actions>
durationduration on the call, or on the host
themeColor="danger"variant="danger"