A beautifully simple, lightweight, and customizable Bottom Sheet Modal for React Native.
If you are tired of dealing with massive dependency trees, complex worklets, and native module linking issues just to get a bottom sheet, this package is for you!
Tip
Why use this?
- 📦 Zero Dependencies: No
react-native-reanimated, noreact-native-gesture-handler. - ⚛️ Pure React Native: Built purely with the native
AnimatedandPanResponderAPIs. - 🚀 Performance: Optimized with
useNativeDriver: truefor buttery smooth 60FPS animations.
- 🚀 100% Dependency Free: Just React and React Native.
- 🪶 Ultra Lightweight: Zero external bridging or heavy third-party animation libraries.
- 🤏 Multi-Snap Points: Define multiple stops (e.g.,
['10%', '50%', '90%']) for your sheet. - 🖐️ Full-Surface Swipability: Swipe up or down from anywhere on the sheet, not just the handlebar.
- 📱 Cross-Platform: Works smoothly on both iOS and Android.
- ⚙️ Highly Customizable: Easily style the backdrop, handle, and content.
- 📦 Modern Architecture: Full support for both ESM and CommonJS.
- 🛡️ TypeScript Ready: First-class type definitions included natively.
npm install @hunter-lab/bottom-sheetor if you prefer yarn:
yarn add @hunter-lab/bottom-sheetimport React, { useState } from "react";
import { View, Button, Text, StyleSheet } from "react-native";
import { BottomSheet } from "@hunter-lab/bottom-sheet";
export default function App() {
const [visible, setVisible] = useState(false);
return (
<View style={styles.container}>
<Button title="Open Bottom Sheet" onPress={() => setVisible(true)} />
<BottomSheet
visible={visible}
onClose={() => setVisible(false)}
snapPoints={["25%", "50%", "90%"]}
initialSnapIndex={0} // Start at 25%
onSnapChange={(index) => console.log("Snapped to:", index)}
>
<View style={styles.content}>
<Text style={styles.title}>Hello from Bottom Sheet!</Text>
<Text>Swipe up to expand, swipe down to minimize or close.</Text>
<Text style={styles.body}>
No reanimated, no gesture handler. Pure React Native performance.
</Text>
</View>
</BottomSheet>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#f5f5f5",
},
content: {
padding: 24,
alignItems: "center",
},
title: {
fontSize: 22,
fontWeight: "700",
color: "#1a1a1a",
marginBottom: 12,
},
body: {
fontSize: 16,
color: "#666",
textAlign: "center",
marginTop: 20,
},
});| Prop | Type | Default | Description |
|---|---|---|---|
visible |
boolean |
required |
Controls the visibility of the modal. |
onClose |
() => void |
required |
Callback fired when the bottom sheet is closed via swipe or backdrop press. |
snapPoints |
(string | number)[] |
['33%'] |
Array of heights. string for % (e.g. '50%'), number for pixels (e.g. 400). |
initialSnapIndex |
number |
0 |
Which snap point index to open at (after auto-sorting ascending). |
onSnapChange |
(index: number) => void |
- |
Callback fired when the sheet snaps to a different point. |
enableSwipeUp |
boolean |
true |
If false, disables swiping up to higher snap points. |
enablePanDownToClose |
boolean |
true |
If false, the sheet will snap back to the lowest point instead of closing. |
animateOnMount |
boolean |
true |
If true, animates the bottom sheet in when first mounted with visible=true. |
hideCloseButton |
boolean |
false |
If true, hides the default "X" close button. |
closeIcon |
ReactNode |
- |
Custom element to replace the default close icon. |
sheetStyle |
StyleProp<ViewStyle> |
- |
Style for the modal sheet container. |
contentStyle |
StyleProp<ViewStyle> |
- |
Style for the content area (inside the sheet). |
backdropStyle |
StyleProp<ViewStyle> |
- |
Style for the darkened backdrop overlay. |
handleIndicatorStyle |
StyleProp<ViewStyle> |
- |
Style for the bar indicator in the handle. |
handleContainerStyle |
StyleProp<ViewStyle> |
- |
Style for the container surrounding the handle bar. |
closeButtonStyle |
StyleProp<ViewStyle> |
- |
Style for the close button wrapper. |
This modal is built with performance in mind. Unlike many other libraries that require complex gesture handlers:
- It uses Native Driver for all opening/closing animations, ensuring they run on the UI thread and stay at 60FPS even when the JS thread is busy.
- It calculates dimensions dynamically using
useWindowDimensionsfor responsiveness. - It is memoized internally to prevent unnecessary re-renders.
- Gesture Support: Uses
PanResponderfor responsive, lag-free dragging across the entire sheet surface.
MIT