rn-modal-bottom-sheet - A performant, gesture-enabled bottom sheet component for React Native with snap points, scroll-to-expand, and pull-to-collapse. Zero native dependencies, works with Expo.
npm install rn-modal-bottom-sheet


rn-modal-bottom-sheet is a performant, gesture-enabled bottom sheet component for React Native with snap points, scroll-to-expand, and pull-to-collapse functionality. Zero native dependencies, works seamlessly with Expo!
> 🎥 Click here to view Demo Video - See the modal sheet in action with snap points, scroll-to-expand, and smooth animations!
- 🎯 Snap Points - Multiple snap positions with intelligent detection
- 📜 Scroll-to-Expand - Automatically expand to next snap point while scrolling
- 👆 Pull-to-Collapse - Pull down at the top to collapse or close
- 🎨 Smooth Animations - Buttery smooth bezier easing with 60fps performance
- 🚀 High Performance - Transform-based animations, no layout recalculations
- 🎯 Zero Native Dependencies - Built with React Native's Animated API
- 📱 Cross Platform - Works on both iOS and Android
- 🎭 Backdrop Animation - Independent opacity animation for backdrop
- 👆 Gesture Support - Drag to close with customizable threshold
- 🎨 Fully Customizable - Customize colors, dimensions, and animations
- 📦 Lightweight - Minimal overhead, no external dependencies
- ♿ ARIA Compliant - Full accessibility support with ARIA attributes
- 🔒 TypeScript Support - Full TypeScript definitions included
``bash`
npm install rn-modal-bottom-sheet react-native-safe-area-contextor
yarn add rn-modal-bottom-sheet react-native-safe-area-context
This package requires the following peer dependencies:
- react-native-safe-area-context (>=3.0.0)react-native-gesture-handler
- (>=2.0.0) - Only needed if using enableDragAndDropreact-native-reanimated
- (>=2.0.0) - Only needed if using SharedValue for snap points
`tsx
import React, { useRef } from 'react';
import { Button, Text, View } from 'react-native';
import ModalSheet, { ModalSheetRef } from 'rn-modal-bottom-sheet';
function App() {
const sheetRef = useRef
return (
Hello Bottom Sheet! 👋
);
}
`
`tsx
import { useRef, useState } from 'react';
function MyComponent() {
const sheetRef = useRef
const [currentIndex, setCurrentIndex] = useState(0);
return (
snapPoints={[0.3, 0.6, 0.9]} // 30%, 60%, 90% of screen height
initialSnapIndex={0}
onSnapPointChange={(index) => setCurrentIndex(index)}
>
);
}
`
`tsx
import { ScrollView } from 'react-native';
snapPoints={[0.3, 0.9]}
enableScrollToExpand={true}
scrollExpandThreshold={20}
>
onScrollBeginDrag={(e) => sheetRef.current?.handleScrollBeginDrag(e)}
onScrollEndDrag={(e) => sheetRef.current?.handleScrollEndDrag(e)}
scrollEventThrottle={16}
>
{/ Your scrollable content /}
`
`tsx
import DraggableFlatList from 'react-native-draggable-flatlist';
height={500}
enableDragAndDrop={true} // Automatically wraps in GestureHandlerRootView
>
renderItem={renderItem}
keyExtractor={(item) => item.key}
onDragEnd={({ data }) => setItems(data)}
/>
`
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| children | ReactNode | Required | Content to be rendered inside the bottom sheet |height
| | number | 400 | Height of the bottom sheet in pixels |snapPoints
| | number[] | - | Array of snap points as percentages (0-1) or pixels |initialSnapIndex
| | number | 0 | Which snap point to open to initially |enableScrollToExpand
| | boolean | true | Enable scroll-to-expand behavior |scrollExpandThreshold
| | number | 50 | Pixels to scroll before triggering transition |enableDragAndDrop
| | boolean | false | Enable automatic GestureHandlerRootView wrapping for gesture components |avoidKeyboard
| | boolean | false | Enable keyboard avoidance to push sheet up when keyboard appears |keyboardOffset
| | number | 0 | Additional offset when keyboard is shown (in pixels) |onSnapPointChange
| | (index: number) => void | - | Callback when snap point changes |onClose
| | () => void | - | Callback when the sheet is closed |onOpen
| | () => void | - | Callback when the sheet is opened |backgroundColor
| | string | 'white' | Background color of the sheet |borderRadius
| | number | 20 | Border radius of the top corners |showHandle
| | boolean | true | Show the drag handle indicator |handleColor
| | string | '#DDD' | Color of the drag handle |backdropOpacity
| | number | 0.5 | Opacity of the backdrop (0-1) |dragThreshold
| | number | 125 | Distance to drag before sheet closes |applyBottomInset
| | boolean | true | Apply bottom safe area inset padding to content |aria-label
| | string | 'Bottom sheet' | Accessible label for the modal |aria-describedby
| | string | - | ID of element describing the modal |backdropAriaLabel
| | string | 'Close bottom sheet' | Accessible label for backdrop |
| Method | Description |
|--------|-------------|
| open() | Opens the bottom sheet |close()
| | Closes the bottom sheet |present()
| | Alias for open() |dismiss()
| | Alias for close() |snapToPoint(index)
| | Snap to a specific snap point by index |handleScroll(event)
| | Process scroll events for scroll-to-expand |handleScrollBeginDrag(event)
| | Track scroll start position |handleScrollEndDrag(event)
| | Handle pull-to-collapse gestures |
`tsx`
height={500}
backgroundColor="#1a1a1a"
borderRadius={30}
handleColor="#666"
backdropOpacity={0.8}
>
`tsx`
snapPoints={[0.15, 0.9]} // Mini player and full player
initialSnapIndex={0}
enableScrollToExpand={true}
>
{/ Your music player UI /}
`tsx`
{items.map((item) => (
))}
`tsx
// The bottom safe area inset is applied by default
{/ Bottom padding automatically applied for devices with home indicator /}
// Disable bottom inset if not needed
`
The scroll-to-expand feature allows users to naturally expand the sheet by scrolling down:
- Gentle scroll down: Expands to next snap point
- Medium swipe down: Jumps 2 snap points
- Fast swipe down: Jumps to maximum height
And collapse by pulling up at the top:
- Gentle pull up: Collapses to previous snap point
- Fast swipe up: Closes the sheet immediately
Fully accessible with WCAG compliance:
- ✅ ARIA Attributes - Modern aria-label, aria-modal, aria-describedby
- ✅ Screen Reader Support - Proper labeling for all interactive elements
- ✅ Keyboard Navigation - Full keyboard support
- ✅ Focus Management - Correct focus handling
- ✅ Gesture Alternatives - Alternative methods for all gestures
- Transform-Based: Uses translateY` transforms for 60fps animations
- Native Driver: All animations run on the UI thread
- Smooth Easing: Custom bezier curve (0.25, 0.1, 0.25, 1)
- No Layout Recalculations: Content pre-rendered once
- Optimized: Efficient re-renders and memory management
- ✅ iOS
- ✅ Android
- ✅ Expo
- ✅ React Native CLI
- GitHub Repository
- Changelog
- Issues
MIT © Christian
If you find this project helpful, consider supporting:

---
Made with ❤️ using React Native