An extremely lightweight, opinionated toast component for React Native.
- Lightweight - only 20KB packed size
- New Architecture - built exclusively for React Native 0.76+ with Fabric
- Clean, imperative API inspired by Sonner
- Zero setup - add one component, start toasting. No hooks, no providers
- Smooth 60fps animations powered by Reanimated
- Natural swipe gestures that feel native to the platform
- Multiple toast types:
success,error,info,promise, andcustom - Promise handling with automatic loading → success/error states
- Toast stacking with configurable limits
- Works above modals - automatic on iOS, simple setup on Android
- RTL support - perfect for Arabic and other RTL languages
- Completely customizable - colors, icons, styles, animations
- Full Expo compatibility
bun add react-native-breadThis package requires the following peer dependencies:
| Package | Version |
|---|---|
| react-native-reanimated | >= 4.1.0 |
| react-native-gesture-handler | >= 2.25.0 |
| react-native-safe-area-context | >= 5.0.0 |
| react-native-screens | >= 4.0.0 |
| react-native-svg | >= 15.8.0 |
| react-native-worklets | >= 0.5.0 |
If you don't have these installed, you can install all peer dependencies at once:
bun add react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-screens react-native-svg react-native-workletsOr with npm:
npm install react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-screens react-native-svg react-native-workletsNote: Make sure your
react-native-reanimatedandreact-native-workletsversions are compatible. Reanimated 4.1.x works with worklets 0.5.x-0.7.x, while Reanimated 4.2.x requires worklets 0.7.x only.
import { BreadLoaf } from 'react-native-bread';
function App() {
return (
<View>
<NavigationContainer>...</NavigationContainer>
<BreadLoaf />
</View>
);
}When using Expo Router, place the BreadLoaf component in your root layout file (app/_layout.tsx):
import { BreadLoaf } from 'react-native-bread';
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<BreadLoaf />
</>
);
}This ensures the toasts will be displayed across all screens in your app.
import { toast } from 'react-native-bread';
// Basic usage
toast.success('Saved!');
// With description
toast.success('Saved!', 'Your changes have been saved');
toast.error('Error', 'Something went wrong');
toast.info('Tip', 'Swipe to dismiss');
// Promise toast - shows loading, then success/error
toast.promise(fetchData(), {
loading: { title: 'Loading...', description: 'Please wait' },
success: { title: 'Done!', description: 'Data loaded' },
error: (err) => ({ title: 'Failed', description: err.message }),
});
// Custom toast - fully custom content with animations
toast.custom(({ dismiss }) => (
<View style={{ padding: 16, flexDirection: 'row', alignItems: 'center' }}>
<Image source={{ uri: 'avatar.png' }} style={{ width: 40, height: 40 }} />
<Text>New message from John</Text>
<Button title="Reply" onPress={dismiss} />
</View>
));Pass an options object as the second argument to customize individual toasts:
toast.success('Saved!', {
description: 'Your changes have been saved',
duration: 5000,
icon: <CustomIcon />,
style: { backgroundColor: '#fff' },
dismissible: true,
showCloseButton: true,
});Create fully custom toasts where you control all the content. Your component fills the entire toast container and receives all entry/exit/stack animations automatically:
// Using a render function (recommended - gives access to dismiss)
toast.custom(({ dismiss, id, type, isExiting }) => (
<View style={{ padding: 16, flexDirection: 'row', alignItems: 'center', gap: 12 }}>
<Image source={{ uri: 'avatar.png' }} style={{ width: 44, height: 44, borderRadius: 22 }} />
<View style={{ flex: 1 }}>
<Text style={{ fontWeight: '600' }}>New message</Text>
<Text style={{ color: '#666' }}>Hey, check this out!</Text>
</View>
<Pressable onPress={dismiss}>
<Text style={{ color: '#3b82f6' }}>Reply</Text>
</Pressable>
</View>
));
// Or pass a React component directly
toast.custom(<MyNotificationCard />);
// With options
toast.custom(<MyToast />, {
duration: 5000,
dismissible: false,
style: { backgroundColor: '#fef2f2' }
});Customize all toasts globally via the config prop on <BreadLoaf />:
<BreadLoaf
config={{
position: 'bottom',
rtl: false, // Enable for RTL languages
stacking: true,
maxStack: 3,
defaultDuration: 4000,
colors: {
success: { accent: '#22c55e', background: '#f0fdf4' },
error: { accent: '#ef4444', background: '#fef2f2' },
}
}}
/>Available options include:
- position:
'top' | 'bottom'- Where toasts appear - offset: Extra spacing from screen edge
- stacking: Show multiple toasts stacked
- maxStack: Max visible toasts when stacking
- dismissible: Allow swipe to dismiss
- showCloseButton: Show X button
- defaultDuration: Default display time in ms
- colors: Custom colors per toast type
- icons: Custom icons per toast type
- toastStyle, titleStyle, descriptionStyle: Global style overrides
| Method | Description |
|---|---|
toast.success(title, description?) |
Show success toast |
toast.error(title, description?) |
Show error toast |
toast.info(title, description?) |
Show info toast |
toast.promise(promise, messages) |
Show loading → success/error toast |
toast.custom(content, options?) |
Show fully custom toast with your own content |
toast.dismiss(id) |
Dismiss a specific toast |
toast.dismissAll() |
Dismiss all toasts |
Toasts automatically appear above native modals on iOS.
On Android, you have two options:
The simplest fix is to use containedModal presentation instead of modal. On Android, modal and containedModal look nearly identical, so this is an easy swap:
<Stack.Screen
name="(modal)"
options={{ presentation: Platform.OS === "android" ? "containedModal" : "modal" }}
/>This renders the modal within the React hierarchy on Android, so toasts from your root <BreadLoaf /> remain visible.
If you need native modals, add <ToastPortal /> inside your modal layouts:
// app/(modal)/_layout.tsx
import { Stack } from "expo-router";
import { ToastPortal } from "react-native-bread";
export default function ModalLayout() {
return (
<>
<Stack screenOptions={{ headerShown: false }} />
<ToastPortal />
</>
);
}The ToastPortal component only renders on Android - it returns null on iOS, so no platform check is needed.
