reactnative-hybrid-bottomsheet
v1.0.0
Published
Hybrid BottomSheet for React Native (Expo + RN CLI, TSX + JSX)
Maintainers
Readme
reactnative-hybrid-bottomsheet
A lightweight, dependency-free hybrid BottomSheet for React Native.
Supports Expo, React Native CLI, iOS, Android, TSX, and JSX.
Why another BottomSheet library?
Most popular bottom sheet libraries depend on Reanimated and Gesture Handler.
This one does not — making it perfect for:
- Expo Managed Workflow
- Lightweight apps
- Simpler projects
- Beginners learning bottom sheet behavior
✅ Zero native setup
✅ Zero additional dependencies
✅ Works everywhere out-of-the-box
Features
- Automatic height based on content
minHeight/maxHeight/fullScreen- Swipe-down gesture to close
- Tap backdrop to close
- Smart scrollable content (only when needed)
- Snap points (
snapPoints) +snapToIndex() initialPosition/initialSnap- Custom drag handle +
dragHandlePosition cornerRadiuscustomization- Imperative API (
open,close,snapToIndex) - Fully typed (TypeScript)
Installation
npm install reactnative-hybrid-bottomsheet
# or
yarn add reactnative-hybrid-bottomsheetBasic Usage (TSX)
import React, { useRef } from "react";
import { View, Button, Text } from "react-native";
import {
BottomSheet,
type BottomSheetMethods,
} from "reactnative-hybrid-bottomsheet";
export default function App() {
const sheetRef = useRef<BottomSheetMethods>(null);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Button title="Open Sheet" onPress={() => sheetRef.current?.open()} />
<BottomSheet
ref={sheetRef}
minHeight="20%"
maxHeight="80%"
snapPoints={["20%", "50%", "80%"]}
initialSnap={1}
scrollable
backdropOpacity={0.5}
dragHandlePosition="top"
cornerRadius={24}
onOpen={() => console.log("Sheet opened")}
onClose={() => console.log("Sheet closed")}
>
<Text style={{ fontSize: 18, marginBottom: 10 }}>
Hybrid BottomSheet 🎉
</Text>
<Text>Snap points, min/max height, scrollable content and more.</Text>
</BottomSheet>
</View>
);
}Usage (JSX)
import React, { useRef } from "react";
import { View, Button, Text } from "react-native";
import { BottomSheet } from "reactnative-hybrid-bottomsheet";
export default function DemoScreen() {
const sheetRef = useRef(null);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Button title="Open" onPress={() => sheetRef.current?.open()} />
<BottomSheet
ref={sheetRef}
fullScreen
enablePanDownToClose
backdropOpacity={0.6}
>
<Text style={{ fontSize: 18, marginBottom: 8 }}>
Full-screen BottomSheet
</Text>
<Text>Works in Expo and React Native CLI.</Text>
</BottomSheet>
</View>
);
}Props
type DragHandlePosition = "top" | "inside" | "hidden";
interface BottomSheetProps {
children?: React.ReactNode;
onClose?: () => void;
onOpen?: () => void;
backdropOpacity?: number;
backdropColor?: string;
enableBackdropPress?: boolean;
disableBackdropPress?: boolean;
animationDuration?: number;
maxHeight?: number | string;
minHeight?: number | string;
initialPosition?: number | string;
fullScreen?: boolean;
expandOnContentChange?: boolean;
autoCloseWhenEmpty?: boolean;
scrollable?: boolean;
snapPoints?: (number | string)[];
initialSnap?: number;
enablePanDownToClose?: boolean;
gestureSensitivity?: number;
handleComponent?: React.ReactNode;
dragHandlePosition?: DragHandlePosition;
cornerRadius?: number;
containerStyle?: StyleProp<ViewStyle>;
contentContainerStyle?: StyleProp<ViewStyle>;
}Props Table
| Prop | Type | Default | Description |
|-------------------------|---------------------------|---------------------------------|-------------|
| children | ReactNode | — | Content inside the sheet |
| onClose | () => void | undefined | Called when sheet fully closes |
| onOpen | () => void | undefined | Called when sheet fully opens |
| backdropOpacity | number | 0.4 | Backdrop darkness |
| backdropColor | string | "rgba(0,0,0,0.4)" | Backdrop color |
| enableBackdropPress | boolean | true | Tap outside to close |
| disableBackdropPress | boolean | false | Force-disable backdrop press |
| animationDuration | number | 300 | Open/close animation time (ms) |
| maxHeight | number \| string | SCREEN_HEIGHT (or fullScreen) | Max height allowed |
| minHeight | number \| string | 0 | Minimum sheet height |
| initialPosition | number \| string | 0 | Initial translateY offset when opening (ignored if snapPoints) |
| fullScreen | boolean | false | If true, sheet uses full screen height |
| expandOnContentChange | boolean | false | Animate height when content size changes |
| autoCloseWhenEmpty | boolean | false | Automatically close when content becomes empty |
| scrollable | boolean | false | Enable smart ScrollView for content |
| snapPoints | (number \| string)[] | [] | Snap offsets (like ["20%", "50%", "80%"]) |
| initialSnap | number | 0 | Initial snap index |
| enablePanDownToClose | boolean | true | Allow swipe-down to close |
| gestureSensitivity | number | 1 | Lower = easier to close via swipe |
| handleComponent | ReactNode | default bar | Custom drag handle content |
| dragHandlePosition | "top" \| "inside" \| "hidden" | "top" | Where to show drag handle |
| cornerRadius | number | 20 | Top corner radius |
| containerStyle | StyleProp<ViewStyle> | undefined | Style override for sheet container |
| contentContainerStyle | StyleProp<ViewStyle> | undefined | Style override for inner content |
Ref Methods
interface BottomSheetMethods {
open(): void;
close(): void;
snapToIndex?: (index: number) => void;
}Demo (GIF Placeholder)
Add your GIF at:
/assets/demo.gifDevelopment
npm install
npm run build
npm packPublishing
npm version patch
npm run build
npm publishLicense
MIT © 2025 Pravin Kumar
