Toast
Use to show feedback to user interactions.
- Automatically closes
- Pause closing on hover, focus, window blur and mobile touch
- Supports closing via swipe gesture
- Easily animatable with Hanzo GUI's animation drivers
- Native toasts included for Android, iOS and web (notification API)
Installation
Toast is included by default in @hanzo/gui. If you're using it standalone:
yarn add @hanzogui/toastNative Toast Setup
For native toasts on iOS/Android (using Burnt by Fernando Rojo), install the native dependency and import the setup module:
yarn add burnt// App.tsx - before any Hanzo GUI imports
import '@hanzogui/native/setup-burnt'Then rebuild your React Native app. Without this setup, toasts will still work but the native prop will have no effect on mobile.
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.
| Prop | Type | Default | Required |
|---|---|---|---|
| label | string | Notification | - |
| duration | number | 5000 | - |
| swipeDirection | SwipeDirection | right | - |
| swipeThreshold | number | 50 | - |
| id | string | A unique generated ID | - |
| native | boolean | ToastNativePlatform | ToastNativePlatform[] | - | - |
| burntOptions | Omit<BurntToastOptions, 'title' | 'message' | 'duration'> | - | - |
| notificationOptions | NotificationOptions | - | - |
ToastViewport
The portal for toasts to be directed to. Should be used inside ToastProvider. Beyond Stack Props, adds:
| Prop | Type | Default | Required |
|---|---|---|---|
| hotkey | string[] | ['F8'] | - |
| label | string | Notifications ({hotkey}) | - |
| name | string | - | - |
| multipleToasts | boolean | - | - |
| portalToRoot | boolean | number | false | - |
| unstyled | boolean | false | - |
Toast
Contains the Title, Description, Action and Close component. Should be used inside ToastProvider. Extends Stack and adds:
| Prop | Type | Default | Required |
|---|---|---|---|
| forceMount | boolean | - | - |
| type | 'foreground' | 'background' | - | - |
| duration | number | - | - |
| defaultOpen | boolean | - | - |
| open | boolean | - | - |
| onOpenChange | (open: boolean): void | - | - |
| onEscapeKeyDown | (): DismissableProps['onEscapeKeyDown'] | - | - |
| onPause | (): void | - | - |
| onResume | (): void | - | - |
| onSwipeStart | (event: SwipeEvent): void | - | - |
| onSwipeMove | (event: SwipeEvent): void | - | - |
| onSwipeCancel | (event: SwipeEvent): void | - | - |
| onSwipeEnd | (event: SwipeEvent): void | - | - |
| viewportName | string | default | - |
| unstyled | boolean | false | - |
Toast.Title
Should be used inside Toast. Extends SizableText, adding:
| Prop | Type | Default | Required |
|---|---|---|---|
| unstyled | boolean | false | - |
Toast.Description
Should be used inside Toast. Extends SizableText, adding:
| Prop | Type | Default | Required |
|---|---|---|---|
| unstyled | boolean | false | - |
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.
| Prop | Type | Default | Required |
|---|---|---|---|
| show | (title: string, showOptions?: ShowToastOptions): void | - | - |
| hide | (): void | - | - |
| options | ToastOptions | - | - |
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
fromtoburntOptions:
<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.myPresetTo add TypeScript auto-completion for your custom fields, you can use TS module augmentation:
declare module '@hanzogui/toast' {
interface CustomData {
myPreset: 'error' | 'success' | 'warning'
}
}Without hooks
You can also use toasts without the hooks.
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>
)
}Last updated on