szn-ui-react-native-bottom-sheet
v1.0.14
Published
A customizable React Native bottom sheet component with gesture support, keyboard awareness, and smooth animations.
Downloads
41
Maintainers
Readme
React Native Bottom Sheet
A smooth, customizable bottom sheet component for React Native with gesture support and TypeScript definitions specifically built for expo SDK 54 and react native reanimated v4 . Ensure that you're using the latest version as previous versions may be broken or depreciated.

Features
- 🎯 TypeScript Support - Full TypeScript definitions included
- 🎨 Customizable - Highly customizable appearance and behavior
- 📱 Cross Platform - Works on both iOS and Android
- ✋ Gesture Support - Smooth drag-to-dismiss functionality
- ⌨️ Keyboard Aware - Proper keyboard handling
- 🎭 Flexible Content - Support for any content with scroll view
- 🎪 Animation - Smooth enter/exit animations
Installation
npm install szn-ui-react-native-bottom-sheetPeer Dependencies
This package requires the following peer dependencies:
npm install react-native-gesture-handler react-native-reanimatedMake sure to follow the installation instructions for:
Usage
import React, { useState } from 'react';
import { View, Text, Button } from 'react-native';
import { BottomSheet } from "szn-ui-react-native-bottom-sheet";
const App = (): JSX.Element => {
const [isVisible, setIsVisible] = useState(false);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Open Bottom Sheet" onPress={() => setIsVisible(true)} />
<BottomSheet
isVisible={isVisible}
onClose={() => setIsVisible(false)}
>
<View style={{ padding: 20 }}>
<Text>Hello from bottom sheet!</Text>
</View>
</BottomSheet>
</View>
);
};
export default App;Props
| Prop | Type | Default | Description |
| ----------------- | ------------ | --------------------- | ------------------------------------------------------------------------ |
| isVisible | boolean | Required | Controls the visibility of the bottom sheet |
| onClose | () => void | Required | Callback when the sheet should be closed |
| children | ReactNode | Required | Content to render inside the bottom sheet |
| height | number | SCREEN_HEIGHT * 0.9 | Height of the bottom sheet |
| backgroundColor | string | 'white' | Background color of the bottom sheet |
| showDragHandle | boolean | true | Whether to show the drag handle |
| containerStyle | ViewStyle | undefined | Additional styles for the sheet container |
| title | string | undefined | Title text to display in the header |
| showHeader | boolean | false | Whether to show the header with title and cancel button |
| cancelText | string | 'Cancel' | Text for the cancel button in header |
You can customize the sheet's background using either the backgroundColor prop or via containerStyle, but if both are provided, the backgroundColor prop takes precedence.
Examples
With Header
<BottomSheet
isVisible={isVisible}
onClose={() => setIsVisible(false)}
showHeader
title="Settings"
cancelText="Done"
>
<View style={{ padding: 20 }}>
<Text>Settings content here</Text>
</View>
</BottomSheet>If you wish to use your own custom close button instead of the cancel text prop
import React, { useRef, useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { BottomSheet, BottomSheetRef } from 'szn-ui-react-native-bottom-sheet';
import { Ionicons } from '@expo/vector-icons';
export default function MyScreen(): JSX.Element {
const [isVisible, setIsVisible] = useState(false);
const sheetRef = useRef<BottomSheetRef>(null);
const handleOpen = () => setIsVisible(true);
const handleClose = () => setIsVisible(false);
return (
<>
<TouchableOpacity onPress={handleOpen}>
<Text>Open Bottom Sheet</Text>
</TouchableOpacity>
<BottomSheet
ref={sheetRef}
isVisible={isVisible}
onClose={handleClose}
showHeader={false}
showDragHandle={true}
height={600}
>
<View className="px-5 pb-3 border-b border-gray-100 flex-row justify-between items-center">
<Text className="text-lg font-semibold text-gray-900">My Custom Header</Text>
<TouchableOpacity
onPress={() => sheetRef.current?.dismiss()}
className="p-2 bg-gray-100 rounded-full"
>
<Ionicons name="close" size={18} color="#374151" />
</TouchableOpacity>
</View>
<Text className="p-5">Sheet Content Here...</Text>
</BottomSheet>
</>
);
}Custom Height
<BottomSheet
isVisible={isVisible}
onClose={() => setIsVisible(false)}
height={400}
>
<View style={{ padding: 20 }}>
<Text>Custom height content</Text>
</View>
</BottomSheet>Multi-Step Flow
const [step, setStep] = useState(1);
return (
<BottomSheet
isVisible={true}
onClose={() => {}}
height={step === 1 ? 200 : step === 2 ? 400 : 600}
>
{step === 1 && <StepOne onNext={() => setStep(2)} />}
{step === 2 && <StepTwo onNext={() => setStep(3)} />}
{step === 3 && <StepThree />}
</BottomSheet>
);Custom Background Color
<BottomSheet
isVisible={isVisible}
onClose={() => setIsVisible(false)}
backgroundColor="#fef2f2"
>
<View style={{ padding: 20 }}>
<Text>Light pink background</Text>
</View>
</BottomSheet>Custom Styling
<BottomSheet
isVisible={isVisible}
onClose={() => setIsVisible(false)}
containerStyle={{
backgroundColor: '#f0f0f0',
borderTopLeftRadius: 30,
borderTopRightRadius: 30,
}}
>
<View style={{ padding: 20 }}>
<Text>Custom styled content</Text>
</View>
</BottomSheet>Full Usage Example
import { useState } from "react";
import {
KeyboardAvoidingView,
Platform,
Pressable,
ScrollView,
StyleSheet,
TextInput,
} from "react-native";
import { BottomSheet } from "szn-ui-react-native-bottom-sheet";
import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view";
import ParallaxScrollView from "@/components/parallax-scroll-view";
import { Image } from "expo-image";
import { HelloWave } from "@/components/hello-wave";
export default function HomeScreen() {
const [isBottomSheetVisible, setIsBottomSheetVisible] = useState(false);
const [inputValue, setInputValue] = useState("");
const openBottomSheet = () => setIsBottomSheetVisible(true);
const closeBottomSheet = () => setIsBottomSheetVisible(false);
return (
<>
<ParallaxScrollView
headerBackgroundColor={{ light: "#A1CEDC", dark: "#1D3D47" }}
headerImage={
<Image
source={require("@/assets/images/partial-react-logo.png")}
style={styles.reactLogo}
/>
}
>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
</ThemedView>
<ThemedView style={styles.stepContainer}>
<Pressable style={styles.bottomSheetButton} onPress={openBottomSheet}>
<ThemedText style={styles.buttonText}>Open Bottom Sheet</ThemedText>
</Pressable>
</ThemedView>
</ParallaxScrollView>
<BottomSheet isVisible={isBottomSheetVisible} onClose={closeBottomSheet}>
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : undefined}
style={{ flex: 1 }}
>
<ScrollView contentContainerStyle={styles.bottomSheetContent}>
<ThemedText type="subtitle">Bottom Sheet Test</ThemedText>
<ThemedText>Your bottom sheet package is working!</ThemedText>
<TextInput
placeholder="Type something here..."
value={inputValue}
onChangeText={setInputValue}
style={styles.textInput}
placeholderTextColor="#999"
/>
<Pressable style={styles.closeButton} onPress={closeBottomSheet}>
<ThemedText style={styles.buttonText}>Close</ThemedText>
</Pressable>
</ScrollView>
</KeyboardAvoidingView>
</BottomSheet>
</>
);
}
const styles = StyleSheet.create({
titleContainer: {
flexDirection: "row",
alignItems: "center",
gap: 8,
},
stepContainer: {
gap: 8,
marginBottom: 8,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: "absolute",
},
bottomSheetButton: {
backgroundColor: "#007AFF",
paddingHorizontal: 20,
paddingVertical: 12,
borderRadius: 8,
alignItems: "center",
marginVertical: 20,
},
buttonText: {
color: "#FFFFFF",
fontWeight: "600",
},
bottomSheetContent: {
padding: 20,
alignItems: "center",
gap: 16,
},
textInput: {
width: "100%",
paddingHorizontal: 16,
paddingVertical: 12,
borderRadius: 8,
borderWidth: 1,
borderColor: "#ccc",
color: "#000",
},
closeButton: {
backgroundColor: "#FF3B30",
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 6,
alignItems: "center",
},
});License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
