Toast

Use to show feedback to user interactions

yarnnpmbunpnpm
import { Toast, useToastController, useToastState } from '@tamagui/toast'
import React from 'react'
import { Button, Label, Switch, XStack, YStack } from 'tamagui'
/**
* IMPORTANT NOTE: if you're copy-pasting this demo into your code, make sure to add:
* - <ToastProvider> at the root
* - <ToastViewport /> where you want to show the toasts
*/
export const ToastDemo = () => {
const [native, setNative] = React.useState(false)
return (
<YStack space alignItems="center">
<ToastControl native={native} />
<CurrentToast />
<NativeOptions native={native} setNative={setNative} />
</YStack>
)
}
const CurrentToast = () => {
const currentToast = useToastState()
if (!currentToast || currentToast.isHandledNatively) return null
return (
<Toast key={currentToast.id} duration={currentToast.duration} enterStyle={{ opacity: 0, scale: 0.5, y: -25 }} exitStyle={{ opacity: 0, scale: 1, y: -20 }} y={0} opacity={1} scale={1} animation="100ms" viewportName={currentToast.viewportName} >
<YStack>
<Toast.Title>{currentToast.title}</Toast.Title>
{!!currentToast.message && (
<Toast.Description>{currentToast.message}</Toast.Description>
)}
</YStack>
</Toast>
)
}
const ToastControl = ({ native }: { native: boolean }) => {
const toast = useToastController()
return (
<XStack gap="$2" justifyContent="center">
<Button onPress={() => { toast.show('Successfully saved!', { message: "Don't worry, we've got your data.", native, }) }} >
Show
</Button>
<Button onPress={() => { toast.hide() }} >
Hide
</Button>
</XStack>
)
}
const NativeOptions = ({
native,
setNative,
}: {
native: boolean
setNative: (native: boolean) => void
}) => {
return (
<XStack gap="$3">
<Label size="$1" onPress={() => setNative(false)}>
Custom
</Label>
<Switch id="native-toggle" nativeID="native-toggle" theme="active" size="$1" checked={!!native} onCheckedChange={(val) => setNative(val)} >
<Switch.Thumb animation={[ 'quick', { transform: { overshootClamping: true, }, }, ]} />
</Switch>
<Label size="$1" onPress={() => setNative(true)}>
Native
</Label>
</XStack>
)
}

Features

  • Automatically closes

  • Pause closing on hover, focus, window blur and mobile touch

  • Supports closing via swipe gesture

  • Easily animatable with Tamagui's animation drivers

  • Native toasts included for Android, iOS and web (notification API)

Installation

Run the following command:

yarn add @tamagui/toast burnt

Then, if targeting native, rebuild your React Native app. React Native requires sub-dependencies with native dependencies always be hoisted to your apps package.json and Toast relies on the amazing Burnt  library by Fernando Rojo to provide its native functionality.

Anatomy

<ToastProvider>
<Toast>
<Toast.Title />
<Toast.Description />
<Toast.Action />
<Toast.Close />
</Toast>
<ToastViewport />
</ToastProvider>

API Reference

ToastProvider

Your toasts should be wrapped within a ToastProvider. This is usually done at the root of your application.

Props

  • label

    string

    Default: 

    Notification

    An author-localized label for each toast. Used to help screen reader users associate the interruption with a toast.

  • duration

    number

    Default: 

    5000

    Time in milliseconds that each toast should remain visible for. This could be overwritten at the toast level as well.

  • swipeDirection

    SwipeDirection

    Default: 

    right

    Direction of pointer swipe that should close the toast.

  • swipeThreshold

    number

    Default: 

    50

    Distance in pixels that the swipe must pass before a close is triggered.

  • id

    string

    Default: 

    A unique generated ID
  • native

    boolean | ToastNativePlatform | ToastNativePlatform[]

    Will show a native toast if is true or is set to the current platform. On iOS, it wraps `SPIndicator` and `SPAlert`. On Android, it wraps `ToastAndroid`. On web, it wraps Notification API. Mobile's native features are handled by `burnt`.

  • burntOptions

    Omit<BurntToastOptions, 'title' | 'message' | 'duration'>

    Options for the burnt package if you're using native toasts on mobile

  • notificationOptions

    NotificationOptions

    Options for the notification API if you're using native toasts on web

  • ToastViewport

    The portal for toasts to be directed to. Should be used inside ToastProvider. Beyond Stack Props, adds:

    Props

  • hotkey

    string[]

    Default: 

    ['F8']

    The keys to use as the keyboard shortcut that will move focus to the toast viewport.

  • label

    string

    Default: 

    Notifications ({hotkey})

    An author-localized label for the toast viewport to provide context for screen reader users when navigating page landmarks. The available `{hotkey}` placeholder will be replaced for you.

  • name

    string

    Used to reference the viewport if you want to have multiple viewports in the same provider.

  • multipleToasts

    boolean

    Pass this when you want to have multiple/duplicated toasts.

  • portalToRoot

    boolean | number

    Default: 

    false

    When true, uses a portal to render at the very top of the root TamaguiProvider.

  • unstyled

    boolean

    Default: 

    false

    When true, remove all default tamagui styling.

  • Toast

    Contains the Title, Description, Action and Close component. Should be used inside ToastProvider. Extends Stack and adds:

    Props

  • forceMount

    boolean

    Used to force mounting when more control is needed. Useful when controlling animation with React animation libraries.

  • type

    'foreground' | 'background'

    Control the sensitivity of the toast for accessibility purposes. For toasts that are the result of a user action, choose foreground. Toasts generated from background tasks should use background.

  • duration

    number

    Time in milliseconds that toast should remain visible for. Overrides value given to `ToastProvider`.

  • defaultOpen

    boolean

    The open state of the dialog when it is initially rendered. Use when you do not need to control its open state.

  • open

    boolean

    The controlled open state of the dialog. Must be used in conjunction with `onOpenChange`.

  • onOpenChange

    (open: boolean): void

    Event handler called when the open state of the dialog changes.

  • onEscapeKeyDown

    (): DismissableProps['onEscapeKeyDown']

    Event handler called when the escape key is down. It can be prevented by calling `event.preventDefault`.

  • onPause

    (): void

    Event handler called when the dismiss timer is paused. On web, this occurs when the pointer is moved over the viewport, the viewport is focused or when the window is blurred. On mobile, this occurs when the toast is touched.

  • onResume

    (): void

    Event handler called when the dismiss timer is resumed. On web, this occurs when the pointer is moved away from the viewport, the viewport is blurred or when the window is focused. On mobile, this occurs when the toast is released.

  • onSwipeStart

    (event: SwipeEvent): void

    Event handler called when starting a swipe interaction. It can be prevented by calling `event.preventDefault`.

  • onSwipeMove

    (event: SwipeEvent): void

    Event handler called during a swipe interaction. It can be prevented by calling `event.preventDefault`.

  • onSwipeCancel

    (event: SwipeEvent): void

    Event handler called at the cancellation of a swipe interaction. It can be prevented by calling `event.preventDefault`.

  • onSwipeEnd

    (event: SwipeEvent): void

    Event handler called at the end of a swipe interaction. It can be prevented by calling `event.preventDefault`.

  • viewportName

    string

    Default: 

    default

    The viewport's name to send the toast to. Used when using multiple viewports and want to forward toasts to different ones.

  • unstyled

    boolean

    Default: 

    false

    When true, remove all default tamagui styling.

  • Toast.Title

    Should be used inside Toast. Extends SizableText, adding:

    Props

  • unstyled

    boolean

    Default: 

    false

    When true, remove all default tamagui styling.

  • Toast.Description

    Should be used inside Toast. Extends SizableText, adding:

    Props

  • unstyled

    boolean

    Default: 

    false

    When true, remove all default tamagui styling.

  • Toast.Close

    Should be used inside Toast. Extends Stack. You can pass asChild to this component and use a custom <Button> inside.

    Toast.Action

    Should be used inside Toast. Extends Stack. You can pass asChild to this component and use a custom <Button> inside.

    useToastController

    Used to control the display of toasts. Should be used inside ToastProvider.

    Returns

  • show

    (title: string, showOptions?: ShowToastOptions): void

    Call it to show a new toast. If you're using native toasts, you can pass native options using `burntOptions` or `notificationOptions` depending on the native platform (mobile/web).

  • hide

    (): void

    Call it to hide the currently displayed toast.

  • options

    ToastOptions

    You can use if need to access the toast options.

  • useToastState

    Used to render out your toast contents. Should be used inside ToastProvider. Doesn't take in anything and returns ToastData.

    const CurrentToast = () => {
    const toast = useToastState()
    // don't show any toast if no toast is present or it's handled natively
    if (!toast || toast.isHandledNatively) {
    return null
    }
    return (
    <Toast key={toast.id} duration={toast.duration} viewport={toast.viewport}>
    <Toast.Title>{toast.title}</Toast.Title>
    <Toast.Description>{toast.message}</Toast.Description>
    </Toast>
    )
    }

    Examples

    Position the viewport

    To position the viewport on native toasts:

    • iOS (burnt): Supports top or bottom placements. Adjustable by passing from to burntOptions:
    <ToastProvider burntOptions={{ from: 'bottom' }}>
    • Android (burnt): Not supported.
    • Web (Notification API): Not supported.

    To position the viewport on custom toasts:

    You should change the positioning of your <ToastViewport>. For instance, if you want them to appear from top right:

    <ToastViewport flexDirection="column-reverse" top={0} right={0} />

    Or for bottom center:

    <ToastViewport flexDirection="column" bottom={0} left={0} right={0} />

    When using multiple toasts, you can change the order of toasts by setting flexDirection to column or column-reverse. Or even have them stack horizontally using row or row-reverse.

    Mobile safe area

    To show toasts inside device's safe area, install react-native-safe-area-context if you haven't, wrap your app inside <SafeAreaProvider>, and then use the safe area insets to position the viewport inside the safe area.

    import { useSafeAreaInsets } from 'react-native-safe-area-context'
    const SafeToastViewport = () => {
    const { left, top, right } = useSafeAreaInsets()
    return (
    <ToastViewport flexDirection="column-reverse" top={top} left={left} right={right} />
    )
    }

    Different viewports

    To send toasts to different viewports, you can set up different viewports:

    const App = () => {
    return (
    <ToastProvider>
    <ToastViewport /> {/* default viewport */}
    <ToastViewport name="viewport-custom" />
    </ToastProvider>
    )
    }

    And then, use the viewport's name on the toasts.

    const MyComponent = () => {
    return <Toast /> // default viewport
    }
    const MyComponent2 = () => {
    return <Toast viewportName="viewport-custom" />
    }

    Custom data

    Just pass your custom data to the second parameter of the show() method.

    const toastController = useToastController()
    toastController.show("Title", { myPreset: 'error' }) // or toastController.show("Title", { customData: { myPreset: 'error' } })

    then, when showing the toast, you can retrieve them like so:

    const toastState = useToastState()
    toastState.myPreset // or toastState.customData.myPreset

    To add TypeScript auto-completion for your custom fields, you can use TS module augmentation:

    declare module '@tamagui/toast' {
    interface CustomData {
    myPreset: 'error' | 'success' | 'warning'
    }
    }

    Without hooks

    You can also use toasts without the hooks.

    You can't use native toasts this way.

    Single Toast

    export default () => {
    const [open, setOpen] = React.useState(false)
    const timerRef = React.useRef(0)
    React.useEffect(() => {
    return () => clearTimeout(timerRef.current)
    }, [])
    return (
    <YStack ai="center">
    <Button onPress={() => { setOpen(false) window.clearTimeout(timerRef.current) timerRef.current = window.setTimeout(() => { setOpen(true) }, 150) }} >
    Single Toast
    </Button>
    <Toast onOpenChange={setOpen} open={open} animation="100ms" enterStyle={{ x: -20, opacity: 0 }} exitStyle={{ x: -20, opacity: 0 }} opacity={1} x={0} >
    <Toast.Title>Subscribed!</Toast.Title>
    <Toast.Description>We'll be in touch.</Toast.Description>
    </Toast>
    </YStack>
    )
    }

    Multiple Toasts

    To use multiple toasts, you should pass multipleToasts to your ToastViewport. Otherwise there'll be issues when swipe-dismissing or animating toasts.

    export default () => {
    const [savedCount, setSavedCount] = React.useState(0)
    return (
    <YStack ai="center">
    <Button onPress={() => { setSavedCount((old) => old + 1) }} >
    Show toast
    </Button>
    {[...Array(savedCount)].map((_, index) => (
    <Toast key={index} animation="100ms" enterStyle={{ x: -20, opacity: 0 }} exitStyle={{ x: -20, opacity: 0 }} opacity={1} x={0} >
    <Toast.Title>Subscribed!</Toast.Title>
    <Toast.Description>We'll be in touch.</Toast.Description>
    </Toast>
    ))}
    </YStack>
    )
    }