DEV Community

Carlos Ramírez
Carlos Ramírez

Posted on

How to use gorhom/react-native-bottom-sheet with Expo Router

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

Github PR

bottom sheet with stack gif

Step by Step guide:

  1. 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

  2. Add the bottom sheet in the layout component (app/stackSheet/_layout.tsx):

    The BottomSheet component should be rendered as the parent of the Stack

    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>
        )
    }
    

  3. Add route files inside the stackSheet folder. (These routes will show up inside the bottom sheet's stack)

  4. 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

Github PR

individual bottom sheet gif

Step by Step guide:

  1. 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

  2. Add the bottom sheet in the layout component (app/bottomSheet/_layout.tsx):

    The BottomSheet component should be rendered as the parent of the Slot

    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>
        )
    }
    
  3. Add route files inside the bottomSheet folder. (These routes will show up as an individual bottom sheet)

  4. 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.

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

Sentry mobile image

Improving mobile performance, from slow screens to app start time

Based on our experience working with thousands of mobile developer teams, we developed a mobile monitoring maturity curve.

Read more

👋 Kindness is contagious

If you found this post useful, please drop a ❤️ or a friendly comment!

Okay.