Recipe - Expo Router Toast Integration
Problem statement
You are using Expo Router and want toasts to persist across route transitions, including modal routes.
Solution approach
Mount ToastProvider + root ToastViewport in app/_layout.tsx, then add a modal-scoped host in modal screens when local layering matters.
Copy-paste code example
// app/_layout.tsx
import { Stack } from "expo-router";
import { ToastProvider, ToastViewport } from "react-native-toast-system";
export default function RootLayout() {
return (
<ToastProvider>
<Stack>
<Stack.Screen name="index" />
<Stack.Screen name="payment-modal" options={{ presentation: "modal" }} />
</Stack>
<ToastViewport />
</ToastProvider>
);
}
// app/payment-modal.tsx
import { Button, View } from "react-native";
import { ToastHost, ToastNativeSurfaceBoundary, useToast } from "react-native-toast-system";
export default function PaymentModalScreen() {
const modalToast = useToast("modal");
return (
<ToastNativeSurfaceBoundary>
<View>
<Button title="Submit payment" onPress={() => modalToast.error("Payment failed")} />
</View>
<ToastHost hostId="modal" />
</ToastNativeSurfaceBoundary>
);
}
Expected behavior
- Root routes use the app-level
ToastViewport. - Modal actions render in
hostId="modal"when targeted withuseToast("modal"). - Navigation changes do not unmount the global toast runtime.
Common pitfall
- Pitfall: mounting
ToastProviderinside a route component. - Fix: keep provider in
app/_layout.tsxso toasts survive route switches.