Tutorial
🚀 Step-by-Step Guide to implement BottomSheet in React Native with Expo and React Navigation.
Video Tutorial​
Watch our detailed video guide for a hands-on implementation walkthrough:
Prerequisites​
Before you begin, make sure you have:
- React Native project set up with Expo
- React Navigation installed
- Basic understanding of React Native and TypeScript
Installation​
Make sure you have these peer dependencies installed in your React Native or Expo project. Using incompatible versions may cause unexpected behavior or crashes:
{
"react": "^18.3.1",
"react-native": "^0.76.7",
"react-native-reanimated": "^3.16.7",
"react-native-gesture-handler": "^2.20.2",
"react-native-safe-area-context": "^4.12.0",
"@react-navigation/native": "^6.x"
}
- Install the package using your preferred package manager:
npm install @masumdev/rn-bottom-sheet
# or
yarn add @masumdev/rn-bottom-sheet
# or
bun add @masumdev/rn-bottom-sheet
- Install required peer dependencies:
npm install react-native-reanimated react-native-gesture-handler react-native-safe-area-context @react-navigation/native
Expo Configuration​
If you're using Expo, use expo install
to ensure compatibility with your Expo SDK version.
- Install dependencies in Expo:
npx expo install react-native-reanimated react-native-gesture-handler react-native-safe-area-context @react-navigation/native
- Update your
babel.config.js
:
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'],
};
};
- Add to your
app.json
orapp.config.js
:
{
"expo": {
"plugins": [
"react-native-reanimated"
]
}
}
Enable Hermes engine in your React Native project for better performance with animations and gestures.
Basic Implementation​
Always place the BottomSheetProvider at the root level of your app, preferably after other providers like NavigationContainer. Incorrect provider placement can cause the bottom sheet to malfunction.
Simple Content​
- First, wrap your app with the BottomSheetProvider:
import { BottomSheetProvider } from '@masumdev/rn-bottom-sheet';
export default function App() {
return (
<BottomSheetProvider>
{/* Your app content */}
</BottomSheetProvider>
);
}
- Use the useBottomSheet hook in your components:
import { useBottomSheet } from '@masumdev/rn-bottom-sheet';
export default function MyComponent() {
const { expand, close, setContent, setSheetTitle } = useBottomSheet();
const handleOpenSheet = () => {
setSheetTitle('My Bottom Sheet');
setContent(
<View style={{ padding: 16 }}>
<Text>Your bottom sheet content here</Text>
</View>
);
expand('50%'); // Opens to 50% of screen height
};
return (
<Button title="Open Bottom Sheet" onPress={handleOpenSheet} />
);
}
Scrollable Content​
Untuk konten yang panjang, gunakan varian ScrollView:
import { useBottomSheet } from '@masumdev/rn-bottom-sheet';
export default function ScrollableContent() {
const { expand, setContent, setSheetTitle, setVariant } = useBottomSheet();
const handleOpenSheet = () => {
setSheetTitle('Scrollable Content');
setVariant('scroll');
setContent(
<View style={{ padding: 16 }}>
{/* Konten panjang di sini */}
{Array(20).fill(0).map((_, i) => (
<Text key={i} style={{ marginBottom: 16 }}>
Item {i + 1}
</Text>
))}
</View>
);
expand('70%');
};
return (
<Button title="Open Scrollable Sheet" onPress={handleOpenSheet} />
);
}
List Content​
Untuk menampilkan daftar item, gunakan varian FlatList:
import { useBottomSheet } from '@masumdev/rn-bottom-sheet';
export default function ListContent() {
const { expand, setSheetTitle, setVariant, setListData, setRenderItem } = useBottomSheet();
const data = Array(50).fill(0).map((_, i) => ({
id: i + 1,
title: `Item ${i + 1}`
}));
const handleOpenSheet = () => {
setSheetTitle('List Content');
setVariant('flatlist');
setListData(data);
setRenderItem(({ item }) => (
<Pressable
onPress={() => console.log(item)}
style={{ padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' }}
>
<Text>{item.title}</Text>
</Pressable>
));
expand('70%');
};
return (
<Button title="Open List Sheet" onPress={handleOpenSheet} />
);
}
Customization Options​
Choose snap points based on your content height and screen size. For forms and detailed content, use higher snap points (60-100%), while for action sheets and simple lists, use lower snap points (30-50%).
Snap Points​
You can customize the height of the bottom sheet using snap points:
// Available snap points: 10% to 90%
expand('70%'); // Opens to 70% of screen height
Styling​
Customize the appearance using provider props:
<BottomSheetProvider
backgroundColor="#ffffff"
backDropColor="rgba(0,0,0,0.5)"
defaultSnapTo="50%"
maxSnapTo="90%"
onStateChange={(isOpen) => console.log('Sheet state:', isOpen)}
>
{/* Your app content */}
</BottomSheetProvider>
Advanced Features​
Gesture Handling​
Bottom sheet mendukung gesture untuk membuka dan menutup:
Best Practices​
Avoid setting complex state or running expensive operations during sheet animations. This can cause performance issues and jerky animations.
-
State Management
- Use the
onStateChange
callback to handle sheet state changes - Keep content updates synchronized with sheet state
- Use the
-
Performance
- Memoize content when possible
- Avoid heavy computations during animations
-
User Experience
- Provide clear visual feedback
- Handle back button/gesture appropriately
- Use appropriate snap points for content size
Advanced Features​
Gesture Handling​
Implement gesture handlers using the react-native-gesture-handler library for smooth interactions. Consider debouncing gesture callbacks for better performance.
import { useBottomSheet } from '@masumdev/rn-bottom-sheet';
const { setContent, expand } = useBottomSheet();
const CustomContent = () => {
const handleSwipeComplete = (direction) => {
if (direction === 'up') {
expand('90%');
} else if (direction === 'down') {
expand('30%');
}
};
return (
<GestureHandlerRootView>
<PanGestureHandler onGestureEvent={handleSwipeComplete}>
<View style={styles.container}>
<Text>Swipe up or down</Text>
</View>
</PanGestureHandler>
</GestureHandlerRootView>
);
};
Custom Animations​
Use Reanimated's native driver animations for optimal performance. Worklets run on the UI thread and provide smooth animations.
import { useBottomSheet } from '@masumdev/rn-bottom-sheet';
import Animated, { withSpring } from 'react-native-reanimated';
const AnimatedContent = () => {
const scale = useSharedValue(1);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: withSpring(scale.value) }],
}));
return (
<Animated.View style={[styles.container, animatedStyle]}>
<Text>Animated Content</Text>
</Animated.View>
);
};
Common Use Cases​
Modal Forms​
const handleOpenForm = () => {
setSheetTitle('New Item');
setContent(
<View style={{ padding: 16 }}>
<TextInput placeholder="Enter item name" />
<Button title="Submit" onPress={handleSubmit} />
</View>
);
expand('60%');
};
Action Sheets​
const handleOpenActions = () => {
setSheetTitle('Choose Action');
setContent(
<View style={{ padding: 16 }}>
<Pressable onPress={() => { /* action 1 */ }}>
<Text>Action 1</Text>
</Pressable>
<Pressable onPress={() => { /* action 2 */ }}>
<Text>Action 2</Text>
</Pressable>
</View>
);
expand('30%');
};
Troubleshooting​
Enable the React Native Debugger and monitor the component lifecycle to identify issues. Check the console for any warnings or errors related to gesture handling or animations.
Common Issues​
-
Sheet not appearing
- Ensure BottomSheetProvider is at the root level
- Check if content is being set correctly
- Verify all peer dependencies are properly installed
-
Gesture handling issues
- Make sure react-native-gesture-handler is properly configured
- Check if GestureHandlerRootView wraps your app
-
Animation performance
- Enable the native driver for animations
- Use Hermes engine for better performance
- Avoid heavy computations during animations
-
Gesture issues
- Verify react-native-reanimated is properly installed
- Check for conflicting gesture handlers
-
Layout problems
- Ensure proper usage of SafeAreaView
- Check for conflicting style properties
For more examples and advanced usage, check out our demo page.