BottomSheet
BottomSheet is the React Native bottom sheet every picker sheet is built on, exported so your own content can use it: filters, menus, confirmations, forms. It slides up over a dimmed backdrop, can be dragged down to dismiss, and is built on React Native's Modal, Animated and PanResponder only.
import { BottomSheet } from '@elik56a/react-native-pickers';
import { useState } from 'react'; import { Pressable, Text, View } from 'react-native'; import { BottomSheet } from '@elik56a/react-native-pickers'; export default function Example() { const [open, setOpen] = useState(false); return ( <> <Pressable onPress={() => setOpen(true)} style={{ backgroundColor: '#C8460D', padding: 14, borderRadius: 12 }}> <Text style={{ color: 'white', fontWeight: '600', textAlign: 'center' }}>Open sheet</Text> </Pressable> <BottomSheet visible={open} onClose={() => setOpen(false)} title="Share" footer={ <Pressable onPress={() => setOpen(false)} style={{ backgroundColor: '#F4F2EE', padding: 14, borderRadius: 12 }}> <Text style={{ textAlign: 'center', fontWeight: '600' }}>Close</Text> </Pressable> } > <View style={{ padding: 16, gap: 12 }}> {['Copy link', 'Send via message', 'Share toβ¦'].map((a) => ( <Text key={a} style={{ fontSize: 16 }}>{a}</Text> ))} </View> </BottomSheet> </> ); }
Try the propsβ
presentation<BottomSheet
visible={open}
onClose={() => setOpen(false)}
title="Share"
draggable
>
β¦
</BottomSheet>
Bottom sheet or modalβ
presentation switches between a bottom sheet and a centered modal:
'auto'(default): a modal on web windows wider than the sheet (688 px), a bottom sheet everywhere else.'bottomSheet': always slides up from the bottom, with a drag handle.'modal': always a centered dialog that fades in, with no drag handle or swipe-to-close.
<BottomSheet presentation="modal" visible={open} onClose={close} title="Confirm">
β¦
</BottomSheet>
Opening and closingβ
BottomSheet is fully controlled: render it with visible and change visible in onClose. onClose is called when the user:
- taps the backdrop (
closeOnBackdropPress, defaulttrue) - drags the sheet down past about 30% of its height (max 140 px), or flicks it down; on the web, scrolls the wheel down over it (
enableSwipeToClose, defaulttrue) - presses Android's back button
When visible turns false, the sheet animates out and then unmounts its children. onOpened and onClosed fire when the animations finish, which is handy for focusing inputs or running work after the sheet is gone.
Headerβ
The header row shows when you pass title, headerLeft or headerRight. A string title is styled for you (use the title slot); a node is rendered as-is. The drag handle and header are both drag areas.
<BottomSheet
visible={open}
onClose={close}
title="Filters"
headerLeft={<Button title="Reset" onPress={reset} />}
headerRight={<Button title="Apply" onPress={apply} />}
>
β¦
</BottomSheet>
Heightβ
- Default: the sheet sizes to its content, up to
maxHeightRatio(default0.9) of the window. heightβ€ 1: a fraction of the window height (height={0.6}β 60%).height> 1: pixels.
With a fixed height, the content area becomes flex: 1, so a FlatList or ScrollView inside fills it and scrolls.
bottomInset adds bottom padding for the home indicator (default 24 on iOS, 12 elsewhere). Pass your safe-area inset for pixel-perfect results:
const insets = useSafeAreaInsets();
<BottomSheet bottomInset={insets.bottom + 8} β¦ />
On tablets and the web the sheet is centred with a maximum width of 640.
Expanding to full screenβ
The sheet can grow past its content height to expandedHeight (default: the whole window below the status bar), and collapse back:
| Expand | Collapse, then close | |
|---|---|---|
| iOS / Android | Drag the handle or header up | Drag it down |
| Web | Wheel up over the sheet, or drag the handle up | Wheel down, or drag the handle down |
Released past halfway, or with a flick, the sheet snaps to the expanded height; otherwise it settles back. A list or scroll view inside keeps the wheel until it reaches its top or bottom, then the sheet takes over.
The handle always drags. On the web the header row doesn't by default (draggable={false}), so mouse drags there still select text; pass draggable to make the whole header a drag area. expandable={false} keeps the sheet at its content height on every platform.
Try it: open the sheet, then scroll the wheel up over it (or drag the header, since this example turns draggable on). Wide web windows show a centered modal by default, so the examples here force presentation="bottomSheet":
import { useState } from 'react'; import { Pressable, ScrollView, Text, View } from 'react-native'; import { BottomSheet } from '@elik56a/react-native-pickers'; const comments = Array.from({ length: 30 }, (_, i) => `Comment #${i + 1}: looks great!`); export default function Example() { const [open, setOpen] = useState(false); return ( <> <Pressable onPress={() => setOpen(true)} style={{ backgroundColor: '#C8460D', padding: 14, borderRadius: 12 }}> <Text style={{ color: 'white', fontWeight: '600', textAlign: 'center' }}>Open comments</Text> </Pressable> <BottomSheet visible={open} onClose={() => setOpen(false)} title="Comments" presentation="bottomSheet" draggable height={0.45} expandedHeight={0.9} > <ScrollView contentContainerStyle={{ padding: 16, gap: 14 }}> {comments.map((c) => ( <Text key={c} style={{ fontSize: 15 }}>{c}</Text> ))} </ScrollView> </BottomSheet> </> ); }
The list scrolls first; once it's back at the top, wheeling up expands the sheet, and wheeling down at the bottom collapses it, then closes it.
Configuring itβ
// Expand to 85% of the window instead of full screen
<BottomSheet expandedHeight={0.85} β¦ />
// Expand to a fixed height in px
<BottomSheet expandedHeight={600} β¦ />
// Mouse dragging on the web too
<BottomSheet draggable β¦ />
// No expanding: drag (or wheel) down still closes it
<BottomSheet expandable={false} β¦ />
// Fixed at its height: no expanding, no swipe or wheel to close
<BottomSheet expandable={false} enableSwipeToClose={false} β¦ />
Every picker sheet takes the same props through sheetProps:
<DatePickerSheet visible={open} onClose={close} sheetProps={{ expandedHeight: 0.8 }} β¦ />
Keyboardβ
On iOS the sheet is wrapped in a KeyboardAvoidingView, so text inputs inside stay visible above the keyboard.
Propsβ
| Prop | Type | Default | Description |
|---|---|---|---|
visible | boolean | required | Show or hide (animated). |
onClose | () => void | required | Requested close: backdrop, swipe, Android back. |
children | ReactNode | required | Sheet content. |
title | ReactNode | β | Header title; strings get the title style. |
headerLeft | ReactNode | β | Left side of the header. |
headerRight | ReactNode | β | Right side of the header. |
footer | ReactNode | β | Content pinned below the children. |
presentation | 'auto' | 'bottomSheet' | 'modal' | 'auto' | Bottom sheet or centered modal; auto is a modal on wide web windows. |
enableSwipeToClose | boolean | true | Drag down (wheel down on the web) to dismiss (bottom sheet only). |
draggable | boolean | true native / false web | The header row drags too, not just the handle. |
expandable | boolean | true | Grow to expandedHeight: drag up on native, wheel up on the web. |
expandedHeight | number | window β status bar | Expanded height: fraction (β€ 1) or px. |
closeOnBackdropPress | boolean | true | Tap outside to dismiss. |
maxHeightRatio | number | 0.9 | Max height as a fraction of the window. |
height | number | content | Fixed height: fraction (β€ 1) or px. |
bottomInset | number | 24 iOS / 12 | Bottom padding. |
onOpened | () => void | β | Open animation finished. |
onClosed | () => void | β | Close animation finished (content unmounted). |
styles | SlotStyles<BottomSheetSlot> | β | Slots below. |
theme | DeepPartial<PickerTheme> | β | Theme override (sheet, backdrop, handle, radius.sheetβ¦). |
testID | string | β | Test ID on the modal. |
Slotsβ
| Slot | Element |
|---|---|
backdrop | Dimmed overlay |
sheet | The sheet surface |
handle | Grab handle |
header | Header row |
title | String title text |
content | Children wrapper |
footer | Footer wrapper |
Theme tokens usedβ
colors.sheet (background), colors.backdrop, colors.handle, radius.sheet (top corners), animation.duration.
Relatedβ
- PickerSheet: a BottomSheet with Cancel / Done buttons
- Filters sheet recipe