rn-modal-bottom-sheet
v2.4.2
Published
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.
Maintainers
Readme
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!
📱 Demo
🎥 Click here to view Demo Video - See the modal sheet in action with snap points, scroll-to-expand, and smooth animations!
✨ Features
- 🎯 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
📦 Installation
npm install rn-modal-bottom-sheet react-native-safe-area-context
# or
yarn add rn-modal-bottom-sheet react-native-safe-area-contextPeer Dependencies
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 usingenableDragAndDropreact-native-reanimated(>=2.0.0) - Only needed if usingSharedValuefor snap points
🚀 Quick Start
Basic Usage
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<ModalSheetRef>(null);
return (
<View style={{ flex: 1 }}>
<Button title="Open Sheet" onPress={() => sheetRef.current?.open()} />
<ModalSheet ref={sheetRef} height={400}>
<Text style={{ fontSize: 24, fontWeight: 'bold', textAlign: 'center' }}>
Hello Bottom Sheet! 👋
</Text>
</ModalSheet>
</View>
);
}With Snap Points
import { useRef, useState } from 'react';
function MyComponent() {
const sheetRef = useRef<ModalSheetRef>(null);
const [currentIndex, setCurrentIndex] = useState(0);
return (
<ModalSheet
ref={sheetRef}
snapPoints={[0.3, 0.6, 0.9]} // 30%, 60%, 90% of screen height
initialSnapIndex={0}
onSnapPointChange={(index) => setCurrentIndex(index)}
>
<Text>Current snap point: {currentIndex}</Text>
</ModalSheet>
);
}With Scroll-to-Expand (NEW in v2.0.0!)
import { ScrollView } from 'react-native';
<ModalSheet
ref={sheetRef}
snapPoints={[0.3, 0.9]}
enableScrollToExpand={true}
scrollExpandThreshold={20}
>
<ScrollView
onScroll={(e) => sheetRef.current?.handleScroll(e)}
onScrollBeginDrag={(e) => sheetRef.current?.handleScrollBeginDrag(e)}
onScrollEndDrag={(e) => sheetRef.current?.handleScrollEndDrag(e)}
scrollEventThrottle={16}
>
{/* Your scrollable content */}
</ScrollView>
</ModalSheet>With Drag & Drop Support (NEW in v2.1.0!)
import DraggableFlatList from 'react-native-draggable-flatlist';
<ModalSheet
ref={sheetRef}
height={500}
enableDragAndDrop={true} // Automatically wraps in GestureHandlerRootView
>
<DraggableFlatList
data={items}
renderItem={renderItem}
keyExtractor={(item) => item.key}
onDragEnd={({ data }) => setItems(data)}
/>
</ModalSheet>📚 API Reference
Props
| 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 |
Methods (via ref)
| 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 |
🎨 Examples
Custom Styling
<ModalSheet
ref={sheetRef}
height={500}
backgroundColor="#1a1a1a"
borderRadius={30}
handleColor="#666"
backdropOpacity={0.8}
>
<Text style={{ color: 'white' }}>Dark Theme Sheet</Text>
</ModalSheet>Music Player (Two Snap Points)
<ModalSheet
ref={sheetRef}
snapPoints={[0.15, 0.9]} // Mini player and full player
initialSnapIndex={0}
enableScrollToExpand={true}
>
{/* Your music player UI */}
</ModalSheet>Scrollable Content
<ModalSheet ref={sheetRef} height={600}>
<ScrollView showsVerticalScrollIndicator={false}>
{items.map((item) => (
<Text key={item.id}>{item.name}</Text>
))}
</ScrollView>
</ModalSheet>With Safe Area Insets
// The bottom safe area inset is applied by default
<ModalSheet ref={sheetRef} height={400}>
<View>
<Text>Content with safe area padding</Text>
{/* Bottom padding automatically applied for devices with home indicator */}
</View>
</ModalSheet>
// Disable bottom inset if not needed
<ModalSheet ref={sheetRef} height={400} applyBottomInset={false}>
<View>
<Text>Content without safe area padding</Text>
</View>
</ModalSheet>🎯 Scroll-to-Expand Behavior
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
♿ Accessibility
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
🚀 Performance
- Transform-Based: Uses
translateYtransforms 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
📱 Platform Support
- ✅ iOS
- ✅ Android
- ✅ Expo
- ✅ React Native CLI
🔗 Links
📄 License
MIT © Christian
☕ Support
If you find this project helpful, consider supporting:
Made with ❤️ using React Native
