@carlosmedina06/react-native-bottom-sheet
v1.0.1
Published
Draggable bottom sheet for React Native with handle-only drag and fit-content mode
Downloads
218
Maintainers
Readme
React Native Bottom Sheet
📱 A draggable bottom sheet for React Native with handle-only drag, fit-content mode, and lifecycle events.
✨ Features
- 👆 Drag from the top handle only (pill)
- 📐
fitContentto size the sheet to content height - 📜 Internal scroll when content exceeds
maxHeight - ❌ Close via backdrop tap, Android back button, or drag
- 🔄 Lifecycle events:
onOpenStart,onOpenEnd,onCloseStart,onCloseEnd,onDrag - 📍 Optional snap points (e.g.
['25%', '50%', '90%']) - ⌨️ Keyboard behavior:
padding,height, ornone - 🛡️ Optional
onCloseRequestto confirm before closing (e.g. unsaved form) - 🎨 Customizable colors, handle, header, footer, and accessibility
- 📘 Written in TypeScript
- ⚡ Compatible with Reanimated v3
- 📱 Compatible with Expo
📦 Installation
yarn add @carlosmedina06/react-native-bottom-sheet@^1📋 Dependencies
This library needs these dependencies to be installed in your project before you can use it:
yarn add react-native-reanimated react-native-gesture-handler🟢 Using Expo?
npx expo install react-native-reanimated react-native-gesture-handler✅ Requirements
| Type | Dependency | Version | Description |
|------|-------------|---------|-------------|
| Required | React | >=18.0.0 | Core UI library |
| Required | React Native | >=0.70.0 | Mobile framework |
| Required | react-native-reanimated | >=3.0.0 | Animations & gestures |
| Required | react-native-gesture-handler | >=2.0.0 | Native gesture handling |
| Optional | react-native-safe-area-context | >=4.0.0 | Safe area insets (notch, home indicator) |
| Optional | react-native-keyboard-controller | >=1.0.0 | Keyboard behavior (e.g. dismiss on sheet close) |
ℹ️ OPTIONAL
These dependencies are not required for the bottom sheet to work. Install them only if you need the behavior they provide.
react-native-safe-area-context (
>=4.0.0) — Use it if you want the sheet to respect safe area insets (notch, home indicator). Follow their installation instructions.react-native-keyboard-controller (
>=1.0.0) — Use it for improved keyboard handling (e.g. dismiss keyboard when closing the sheet by drag). Follow their installation instructions.
ℹ️ INFO
React Native Gesture Handler v2 needs extra steps to finalize its installation. Please follow their installation instructions.
Please make sure to wrap your App with
GestureHandlerRootViewwhen you've upgraded to React Native Gesture Handler^2.React Native Reanimated v3 needs extra steps to finalize its installation. Please follow their installation instructions.
Example — wrap your app with GestureHandlerRootView:
import { GestureHandlerRootView } from 'react-native-gesture-handler'
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* Your app content */}
</GestureHandlerRootView>
)
}🚀 Usage
import { useState } from 'react'
import { Text, View } from 'react-native'
import BottomSheet from '@carlosmedina06/react-native-bottom-sheet'
export default function Example() {
const [open, setOpen] = useState(false)
return (
<BottomSheet
open={open}
onClose={() => setOpen(false)}
fitContent
minHeight="20%"
maxHeight="90%"
onOpenStart={() => console.log('open:start')}
onOpenEnd={() => console.log('open:end')}
onCloseStart={() => console.log('close:start')}
onCloseEnd={() => console.log('close:end')}
onDrag={(event) => {
// event.progress => 0..1
}}
>
<View>
<Text>Content</Text>
</View>
</BottomSheet>
)
}📚 API
📤 Exports
default→BottomSheettype BottomSheetPropstype BottomSheetDragEvent
⚙️ Props
| Prop | Type | Default | Description |
|------|------|--------|-------------|
| open | boolean | required | Controls sheet visibility |
| onClose | () => void | required | Called when close is requested (backdrop, back, or drag) |
| onOpen | () => void | undefined | Called when opening starts |
| onOpenStart | () => void | undefined | Called when open transition starts |
| onOpenEnd | () => void | undefined | Called when open animation ends |
| onCloseStart | () => void | undefined | Called when close transition starts |
| onCloseEnd | () => void | undefined | Called when close animation ends |
| onDrag | (event: BottomSheetDragEvent) => void | undefined | Called on each drag update |
| children | ReactNode | required | Sheet content |
| minHeight | number \| string | 220 | Min height (number or %) |
| maxHeight | number \| string | 95% of window | Max height (number or %) |
| fitContent | boolean | false | Size to content up to maxHeight |
| keyboardBehavior | 'padding' \| 'height' \| 'none' | 'none' | Keyboard offset behavior |
| enablePanDownToClose | boolean | true | Allow close by dragging down |
| testID | string | undefined | Test identifier |
| title | string \| ReactNode | undefined | Optional title below handle |
| accessibilityLabel | string | undefined | A11y label for sheet container |
| accessibilityRole | AccessibilityRole | undefined | A11y role (e.g. 'dialog') |
| handleAccessibilityLabel | string | 'Drag to close' | A11y label for handle |
| backdropColor | string | 'rgba(0,0,0,0.4)' | Backdrop color |
| handleColor | string | '#D1D5DB' | Handle pill color |
| sheetBackgroundColor | string | '#fff' | Sheet panel background |
| borderTopRadius | number | 12 | Top corner radius |
| renderHandle | () => ReactNode | undefined | Custom handle; default is pill |
| renderHeader | () => ReactNode | undefined | Custom header above title |
| renderFooter | () => ReactNode | undefined | Custom footer below content |
| onCloseRequest | () => boolean \| Promise<boolean> | undefined | Called before close; return false to prevent close |
| snapPoints | (number \| string)[] | undefined | Snap heights (e.g. ['30%', '60%', '100%']); ignored if fitContent is true |
📊 BottomSheetDragEvent
| Field | Type | Description |
|-------|------|-------------|
| translateY | number | Current Y translation of the sheet |
| height | number | Current visible sheet height |
| progress | number | Open progress 0..1 |
💡 Examples
🎨 Custom colors and handle:
<BottomSheet
open={open}
onClose={() => setOpen(false)}
sheetBackgroundColor="#f5f5f5"
handleColor="#6366f1"
renderHandle={() => <MyCustomHandle />}
>
{children}
</BottomSheet>🛡️ Prevent close (onCloseRequest):
<BottomSheet
open={open}
onClose={() => setOpen(false)}
onCloseRequest={async () => {
if (hasUnsavedChanges) {
const ok = await Alert.alert('Discard changes?', '...', [...])
return ok
}
return true
}}
>
{children}
</BottomSheet>📍 Snap points (without fitContent):
<BottomSheet
open={open}
onClose={() => setOpen(false)}
snapPoints={['25%', '50%', '90%']}
>
{children}
</BottomSheet>🔀 Event order
- Open:
onOpenStart→onOpen→onOpenEnd - Close: if
onCloseRequestis set, it runs first; close only proceeds when it returnstrue. ThenonCloseStart→onCloseEnd. WithoutonCloseRequest, close runs directly. - Drag:
onDragfires while dragging from the handle.
⌨️ Keyboard (iOS)
With keyboardBehavior other than 'none':
- iOS: The sheet background extends behind the keyboard; content translates up. Closing the sheet by drag also dismisses the keyboard with the close animation.
- Android: The whole sheet moves up with the keyboard. Drag-to-close does not specially dismiss the keyboard.
📖 Behavior
- With
fitContent=true, the sheet height follows content.snapPointsis ignored whenfitContentis true. - With
snapPoints(and nofitContent), the sheet snaps to the nearest point on release; if the nearest is the minimum and threshold/velocity is met, it closes. - When content is taller than
maxHeight, internal scroll is enabled. - Dragging only works from the top handle (or custom
renderHandle). - Close by gesture uses distance and/or velocity;
onCloseRequestis respected before closing. - Colors and radii for sheet, handle, and backdrop are configurable via props.
🤝 Contributing
Contributions are welcome. Please read CONTRIBUTING.md for setup, workflow, and code expectations.
