Gorhom Bottom Sheet vs Expo Bottom Sheet vs RNSBS 2026
Gorhom Bottom Sheet vs Expo Bottom Sheet vs RNSBS 2026
TL;DR
Bottom sheets are one of the most-used mobile UI patterns — filter panels, share menus, detail views — and the React Native ecosystem has multiple solid options. Gorhom Bottom Sheet v5 is the most feature-complete: Reanimated-powered smooth animations, gesture-controlled dragging, snap points, dynamic content sizing, and a BottomSheetFlatList/BottomSheetScrollView that properly handles scrollable content inside sheets. Expo Bottom Sheet is a wrapper around Gorhom built for the Expo/expo-ui ecosystem — simplifies the API for common use cases and integrates with the Expo design system. React Native Simple Bottom Sheet (RNSBS) is the lightweight option — minimal dependencies, simple snap-to-open/close behavior, and easy integration when you just need a basic sheet without the full Gorhom power. For complex sheets with scrollable content, multiple snap points, and gestures: Gorhom. For Expo-native apps wanting a simpler API: Expo Bottom Sheet. For simple, minimal-dependency sheets: RNSBS.
Key Takeaways
- Gorhom v5 uses Reanimated 3 — worklet-based animations run on UI thread, not JS thread
- Gorhom supports dynamic sizing —
enableDynamicSizingmeasures content height automatically - BottomSheetFlatList — scroll-inside-sheet with proper gesture discrimination
- Snap points —
["25%", "50%", "90%"]or pixel values[300, 600] - Backdrop component — dimmed overlay that dismisses sheet on tap
- Expo Bottom Sheet uses Gorhom under the hood — same performance characteristics
- RNSBS has no Reanimated dependency — uses React Native
AnimatedAPI
Use Case Guide
Scrollable list inside sheet → Gorhom BottomSheetFlatList
Dynamic height (auto-size) → Gorhom enableDynamicSizing
Multiple snap positions → Gorhom snapPoints prop
Simple open/close sheet → RNSBS or Expo Bottom Sheet
Expo project (managed workflow) → Expo Bottom Sheet
Filter panel with form → Gorhom BottomSheetScrollView
Confirmation / action sheet → Any (RNSBS simplest)
Full-screen drawer behavior → Gorhom at snapPoints={["100%"]}
Gorhom Bottom Sheet v5
The industry standard for React Native bottom sheets — smooth Reanimated animations, gesture-based dragging, and first-class support for scrollable content inside the sheet.
Installation
npm install @gorhom/bottom-sheet@5
# Required dependencies:
npm install react-native-reanimated react-native-gesture-handler
npx pod-install # iOS
Gesture Handler Setup
// app/_layout.tsx or App.tsx
import { GestureHandlerRootView } from "react-native-gesture-handler";
export default function RootLayout() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* rest of app */}
</GestureHandlerRootView>
);
}
Basic Bottom Sheet
import { useRef, useCallback } from "react";
import { View, Text, Button, StyleSheet } from "react-native";
import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet";
export function FilterPanel() {
const bottomSheetRef = useRef<BottomSheet>(null);
// Snap to 50% on open, dismiss on close
const snapPoints = ["50%", "90%"];
const handleOpen = useCallback(() => {
bottomSheetRef.current?.expand();
}, []);
const handleClose = useCallback(() => {
bottomSheetRef.current?.close();
}, []);
const handleSheetChanges = useCallback((index: number) => {
console.log("Sheet position changed to index:", index);
}, []);
return (
<View style={styles.container}>
<Button title="Open Filters" onPress={handleOpen} />
<BottomSheet
ref={bottomSheetRef}
index={-1} // -1 = closed (hidden)
snapPoints={snapPoints}
onChange={handleSheetChanges}
enablePanDownToClose={true}
>
<BottomSheetView style={styles.contentContainer}>
<Text style={styles.title}>Filters</Text>
<Button title="Apply" onPress={handleClose} />
</BottomSheetView>
</BottomSheet>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
contentContainer: { flex: 1, padding: 20, alignItems: "center" },
title: { fontSize: 18, fontWeight: "bold", marginBottom: 16 },
});
Backdrop (Dimmed Overlay)
import BottomSheet, { BottomSheetBackdrop, BottomSheetView } from "@gorhom/bottom-sheet";
import { useCallback } from "react";
export function BottomSheetWithBackdrop() {
const renderBackdrop = useCallback(
(props: any) => (
<BottomSheetBackdrop
{...props}
disappearsOnIndex={-1}
appearsOnIndex={0}
pressBehavior="close" // Tap backdrop to close
opacity={0.5}
/>
),
[]
);
return (
<BottomSheet
index={-1}
snapPoints={["50%"]}
backdropComponent={renderBackdrop}
enablePanDownToClose
>
<BottomSheetView style={{ padding: 20 }}>
<Text>Sheet content</Text>
</BottomSheetView>
</BottomSheet>
);
}
Scrollable Content (BottomSheetFlatList)
import BottomSheet, { BottomSheetFlatList } from "@gorhom/bottom-sheet";
interface Product {
id: string;
name: string;
price: number;
}
export function ProductSheet({ products }: { products: Product[] }) {
const renderItem = useCallback(
({ item }: { item: Product }) => (
<View style={styles.item}>
<Text>{item.name}</Text>
<Text>${item.price}</Text>
</View>
),
[]
);
return (
<BottomSheet
index={0}
snapPoints={["50%", "90%"]}
enablePanDownToClose
>
{/* BottomSheetFlatList handles gesture discrimination
between sheet drag and list scroll automatically */}
<BottomSheetFlatList
data={products}
keyExtractor={(item) => item.id}
renderItem={renderItem}
contentContainerStyle={styles.listContent}
/>
</BottomSheet>
);
}
Dynamic Height (Auto-Size Content)
import BottomSheet, { BottomSheetView } from "@gorhom/bottom-sheet";
export function AutoSizedSheet() {
return (
<BottomSheet
index={0}
enableDynamicSizing={true} // Height matches content
enablePanDownToClose
>
<BottomSheetView>
{/* Sheet height adjusts to fit this content */}
<Text style={{ padding: 20, fontSize: 16 }}>
This sheet auto-sizes to fit its content.
</Text>
<View style={{ padding: 20 }}>
<Button title="Action 1" onPress={() => {}} />
<Button title="Action 2" onPress={() => {}} />
<Button title="Cancel" onPress={() => {}} />
</View>
</BottomSheetView>
</BottomSheet>
);
}
BottomSheetModal (Global Modal System)
import { useCallback, useRef } from "react";
import {
BottomSheetModal,
BottomSheetModalProvider,
BottomSheetView,
} from "@gorhom/bottom-sheet";
// Wrap app with BottomSheetModalProvider
export function AppWithModalProvider() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<BottomSheetModalProvider>
<App />
</BottomSheetModalProvider>
</GestureHandlerRootView>
);
}
// Use the modal from any component
function ProductCard({ product }: { product: Product }) {
const modalRef = useRef<BottomSheetModal>(null);
const handlePresent = useCallback(() => {
modalRef.current?.present();
}, []);
const handleDismiss = useCallback(() => {
modalRef.current?.dismiss();
}, []);
return (
<View>
<TouchableOpacity onPress={handlePresent}>
<Text>{product.name}</Text>
</TouchableOpacity>
<BottomSheetModal
ref={modalRef}
snapPoints={["60%"]}
enablePanDownToClose
>
<BottomSheetView style={{ padding: 20 }}>
<Text style={{ fontSize: 20, fontWeight: "bold" }}>{product.name}</Text>
<Text>Price: ${product.price}</Text>
<Button title="Add to Cart" onPress={handleDismiss} />
</BottomSheetView>
</BottomSheetModal>
</View>
);
}
Expo Bottom Sheet
Expo's bottom sheet component from the expo-ui ecosystem — a simpler API wrapping Gorhom for common Expo use cases.
Installation
npx expo install expo-bottom-sheet
Usage
import { BottomSheet, BottomSheetView } from "expo-bottom-sheet";
import { useState } from "react";
export function SimpleSheet() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button title="Open" onPress={() => setIsOpen(true)} />
<BottomSheet
isOpen={isOpen}
onClose={() => setIsOpen(false)}
>
<BottomSheetView>
<Text style={{ padding: 20 }}>Sheet content here</Text>
<Button title="Close" onPress={() => setIsOpen(false)} />
</BottomSheetView>
</BottomSheet>
</>
);
}
React Native Simple Bottom Sheet (RNSBS)
A lightweight bottom sheet with no Reanimated dependency — uses React Native's built-in Animated API for a simple open/close behavior.
Installation
npm install rn-simple-bottom-sheet
Usage
import RNSimpleBottomSheet from "rn-simple-bottom-sheet";
import { useRef } from "react";
export function SimpleSheet() {
const sheetRef = useRef<RNSimpleBottomSheet>(null);
return (
<View style={{ flex: 1 }}>
<Button title="Open" onPress={() => sheetRef.current?.open()} />
<RNSimpleBottomSheet
ref={sheetRef}
height={300}
draggable={true}
onClose={() => console.log("Sheet closed")}
>
<View style={{ padding: 20 }}>
<Text>Simple sheet content</Text>
<Button title="Close" onPress={() => sheetRef.current?.close()} />
</View>
</RNSimpleBottomSheet>
</View>
);
}
Feature Comparison
| Feature | Gorhom v5 | Expo Bottom Sheet | RNSBS |
|---|---|---|---|
| Animation engine | Reanimated 3 (UI thread) | Reanimated 3 | RN Animated (JS thread) |
| Snap points | ✅ Multiple | ✅ | ❌ (height only) |
| Dynamic sizing | ✅ enableDynamicSizing | ✅ | ❌ |
| Scrollable content | ✅ BottomSheetFlatList | ✅ | ⚠️ Manual |
| Backdrop | ✅ BottomSheetBackdrop | ✅ | ✅ |
| BottomSheetModal | ✅ (global modal system) | ✅ | ❌ |
| Gesture pan-to-dismiss | ✅ | ✅ | ✅ |
| Expo Go compatible | ❌ (Reanimated) | ✅ | ✅ |
| Setup complexity | Medium (GestureHandler) | Low | Low |
| Bundle size | Large | Medium | Small |
| TypeScript | ✅ Excellent | ✅ | ✅ |
| GitHub stars | 7.5k | N/A (new) | 0.5k |
| npm weekly | 700k | Growing | 20k |
When to Use Each
Choose Gorhom Bottom Sheet if:
- Scrollable list/FlatList inside the sheet (proper gesture discrimination)
- Multiple snap points (25%, 50%, 90%) for expanding/collapsing
enableDynamicSizingto fit variable-height contentBottomSheetModalfor a centralized modal system- Full control over backdrop, handle, and animation
Choose Expo Bottom Sheet if:
- Building with Expo SDK and want API consistency with
expo-uicomponents - Standard open/close behavior is sufficient — no complex snap points needed
- Prefer Expo's managed approach to native modules
Choose RNSBS if:
- Zero tolerance for Reanimated/Gesture Handler peer dependencies
- Works in Expo Go (no bare workflow needed)
- Simple open/close action sheet behavior — no scrollable content
- Lightweight dependencies are a priority
Methodology
Data sourced from Gorhom Bottom Sheet documentation (gorhom.github.io/react-native-bottom-sheet), Expo Bottom Sheet documentation (docs.expo.dev), React Native Simple Bottom Sheet GitHub repository, npm download statistics as of February 2026, GitHub star counts as of February 2026, and community discussions from the React Native Discord and r/reactnative.
Related: FlashList vs FlatList vs LegendList for the high-performance list components used inside BottomSheetFlatList, or React Native Reanimated vs Moti vs Skia for the animation library that powers Gorhom's smooth sheet transitions.