I was recently revamping an app to replace some route based modals with bottom sheets, so I decided to use the gorhom/react-native-bottom-sheet library. While exploring the integration, I came across a few discussions and issues around how to effectively use bottom sheets within an Expo Router setup.
In this post, I’ll walk you through two different approaches you can use as a starting point to render a route based bottom-sheet in your app. I created the expo-router-gorhom-bottom-sheet-examples repository which includes functional code for both approaches. Feel free to follow along with this post, or head straight to the repository to check out the examples.
This article assumes that you already have gorhom/react-native-bottom-sheet
installed in your expo repo
Approach 1: Single Bottom Sheet with Nested Routes
Step by Step guide:
-
Add a top-level transparent screen:
// app/_layout.tsx <Stack.Screen name="stackSheet" options={{ headerShown: false, // 'animation' is disabled here since the bottom sheet already has its own animation. animation: "none", // This is important so that the bottom sheet is displayed // with a transparent background (Not as a separate route). presentation: "transparentModal", }} />
*This ensures the bottom sheet renders above your main content while still showing the background screen
-
Add the bottom sheet in the layout component (
app/stackSheet/_layout.tsx
):The
BottomSheet
component should be rendered as the parent of theStack
See stackSheet/_layout.tsx code
const Approach1Layout = () => { const router = useRouter() const snapPoints = useMemo(() => ["50%", "80%"], []) // Renders a backdrop so that the sheet closes if user clicks outside of the sheet const renderBackdrop = useCallback((props: BottomSheetBackdropProps) => { return <BottomSheetBackdrop {...props} disappearsOnIndex={-1} opacity={0.35} /> }, []) return ( <BottomSheet enableDynamicSizing={false} backdropComponent={renderBackdrop} snapPoints={snapPoints} onClose={() => router.dismissTo("/")} > <BottomSheetView style={styles.container}> {/* Expo router's Stack inside BottomSheet */} <Stack initialRouteName="index" screenOptions={{ contentStyle: { flex: 1, backgroundColor: "white" } }} > <Stack.Screen name="index" options={{ headerShown: false, headerTitle: "Approach 1" }} /> <Stack.Screen name="route1" options={{ headerTitle: "Route 1" }} /> <Stack.Screen name="route2" options={{ headerTitle: "Route 2" }} /> <Stack.Screen name="route3" options={{ headerTitle: "Route 3" }} /> </Stack> </BottomSheetView> </BottomSheet> ) }
Add route files inside the
stackSheet
folder. (These routes will show up inside the bottom sheet's stack)Done! If you click on a
<Link href="/stackSheet/route1">
it will take you to the bottom sheet stack
Approach #2: Individual Route-based Bottom Sheets
Step by Step guide:
-
Add a top-level
bottomSheet
transparent screen:
// app/_layout.tsx <Stack.Screen name="bottomSheet" options={{ headerShown: false, // 'animation' is disabled here since the bottom sheet already has its own animation. animation: "none", // This is important so that the bottom sheet is displayed // with a transparent background (Not as a separate route). presentation: "transparentModal", }} />
*This ensures the bottom sheet renders above your main content while still showing the background screen
-
Add the bottom sheet in the layout component (
app/bottomSheet/_layout.tsx
):The
BottomSheet
component should be rendered as the parent of theSlot
const Approach2Layout = () => { // Renders a backdrop so that the sheet closes if user clicks outside of the sheet const renderBackdrop = useCallback((props: BottomSheetBackdropProps) => { return <BottomSheetBackdrop {...props} disappearsOnIndex={-1} opacity={0.35} /> }, []) return ( <BottomSheet backdropComponent={renderBackdrop} onClose={router.back}> <BottomSheetView style={styles.container}> {/* Expo router's Slot inside BottomSheet */} <Slot /> </BottomSheetView> </BottomSheet> ) }
Add route files inside the
bottomSheet
folder. (These routes will show up as an individual bottom sheet)Done! If you click on a
<Link href="/bottomSheet/route1">
it will take you to the bottom sheet
Conclusion
We explored two approaches for integrating gorhom/react-native-bottom-sheet
with Expo Router. Choosing between a single bottom sheet with nested routes or individual bottom sheets depends on your app's complexity and customization needs.
These approaches simplify bottom sheet handling by leveraging Expo Router's built-in routing, eliminating the need to manually manage states using methods like dismiss
, present
or snapToIndex
outlined in BottomSheetModal.
However, using BottomSheetModal
remains valid and practical when you don't need a route-based solution or if you prefer managing all the state within the component without involving route-based navigation.
Top comments (0)