Picker sheets
Every React Native picker in this library is also available inside a BottomSheet with Cancel / Done: in the header beside the title on iOS, under the content in Material (as OK) and on the web, per the design. They all share the same "chrome" props and the same draft behaviour.
This page covers DatePickerSheet, TimePickerSheet, WheelPickerSheet, EmojiPickerSheet and the PickerSheet building block. The other sheets are on their picker's page: DateTimePickerSheet, TimeRangePickerSheet, DurationPickerSheet, MonthPickerSheet and ColorPickerSheet.
You don't need your own button and visible state. Picker fields (DatePickerField, TimePickerField, TimeRangePickerField, DateTimePickerField, DurationPickerField, MonthPickerField, ColorPickerField, WheelPickerField, EmojiPickerField) take the same props without visible/onClose, render a field that opens the sheet, and show the confirmed value.
Inline, bottom sheet or modal​
Every picker can be shown three ways:
- Inline: the plain component (
WheelPicker,TimePicker,DatePicker,SelectList,EmojiPicker) renders in place. - Bottom sheet: slides up from the bottom and can be dragged down to dismiss.
- Modal: a centered dialog.
Each sheet can also open from a field that shows the picked value.
The sheets take presentation: 'auto' (default), 'bottomSheet' or 'modal'. auto is a modal on web windows wider than the sheet, where a bottom sheet would look cut off, and a bottom sheet on phones and native.
<TimePickerSheet presentation="modal" visible={open} onClose={close} value={time} onConfirm={setTime} />
SelectPicker and the fields also support 'dropdown', which opens under the field, and 'inline', which expands under it. There, 'auto' opens a dropdown on the web.
import { DatePickerSheet, TimePickerSheet, WheelPickerSheet, EmojiPickerSheet, PickerSheet, useDraft } from '@elik56a/react-native-pickers';
Try the props​
presentationmodedisplay<DatePickerSheet
visible={open}
onClose={() => setOpen(false)}
value={value}
onConfirm={setValue}
/>
How drafts work​
- When the sheet opens, it copies
valueinto a draft. - The picker inside edits only the draft.
- Done calls
onConfirm(draft)and thenonClose(). - Cancel, a backdrop tap, a swipe down or Android back calls only
onClose(), and the draft is thrown away.
Your state changes only when the user confirms, which is how native iOS / Android pickers behave. The exception is a field set to presentation="inline": it has no Done, so each change is confirmed right away.
DatePickerSheet​
A calendar (single, range, multiple or ranges) or a date wheel in a sheet. Props: DatePickerSheet props.
<View style={{ gap: 10 }}> <Field label="Delivery date" value={day ? formatDate(day) : 'Choose…'} onPress={() => setSheet('day')} /> <Field label="Trip" value={ range.start && range.end ? `${formatDate(range.start)} → ${formatDate(range.end)}` : 'Choose…' } onPress={() => setSheet('range')} /> <Field label="Birthday" value={formatDate(birthday)} onPress={() => setSheet('birthday')} /> <DatePickerSheet visible={sheet === 'day'} onClose={close} title="Delivery" value={day} onConfirm={setDay} minDate={new Date()} confirmOnSelect /> <DatePickerSheet visible={sheet === 'range'} onClose={close} title="Trip dates" mode="range" value={range} onConfirm={setRange} minDate={new Date()} /> <DatePickerSheet visible={sheet === 'birthday'} onClose={close} title="Birthday" display="wheel" value={birthday} onConfirm={setBirthday} maxDate={new Date()} /> </View>
Behaviour​
- Done is disabled until the selection is complete: a date in single mode, both
startandendin range mode, no half-picked range in ranges mode. Multiple and ranges modes can confirm an empty array. confirmOnSelect(single + calendar only) hides Done and confirms as soon as a day is tapped.display="wheel"shows a DateWheelPicker (single mode). The draft starts atvalueor today.locale,minDateandmaxDateare forwarded; pass other wheel props viawheelProps.- In the Material design, the picked date or range shows in large type above the calendar.
TimePickerSheet​
A TimePicker in a sheet. Props: TimePickerSheet props.
<> <Pressable onPress={() => setOpen(true)} style={{ alignItems: 'center', padding: 12 }} > <Text style={{ fontSize: 48, fontWeight: '200' }}> {time.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })} </Text> <Text style={{ color: '#C8460D' }}>Change</Text> </Pressable> <TimePickerSheet visible={open} onClose={() => setOpen(false)} title="Wake up" value={time} onConfirm={setTime} minuteInterval={5} /> </>
WheelPickerSheet​
A WheelPicker in a sheet. Props: WheelPickerSheet props.
<> <Pressable onPress={() => setOpen(true)} style={{ borderWidth: 1, borderColor: '#E8E4DC', borderRadius: 12, padding: 14, }} > <Text>👥 {guests[count - 1].label}</Text> </Pressable> <WheelPickerSheet visible={open} onClose={() => setOpen(false)} title="Guests" items={guests} value={count} onConfirm={setCount} /> </>
EmojiPickerSheet​
No Done button: each pick calls onSelect, and the sheet closes unless closeOnSelect={false}. Props: EmojiPickerSheet props.
<View style={{ flexDirection: 'row', gap: 8, flexWrap: 'wrap' }}> {reactions.map((r, i) => ( <Text key={i} style={{ fontSize: 22, backgroundColor: '#F4F2EE', borderRadius: 999, paddingHorizontal: 10, paddingVertical: 4, }} > {r} </Text> ))} <Pressable onPress={() => setOpen(true)} style={{ borderRadius: 999, borderWidth: 1, borderColor: '#E8E4DC', paddingHorizontal: 12, justifyContent: 'center', }} > <Text>+</Text> </Pressable> <EmojiPickerSheet visible={open} onClose={() => setOpen(false)} title="React" onSelect={(e) => setReactions((r) => [...r, e])} /> </View>
PickerSheet​
The building block: a BottomSheet with Cancel and, when you pass onConfirm, Done, placed where the design puts them. Use it to build sheets for your own pickers. Props: PickerSheet props.
<> <Pressable onPress={() => setOpen(true)} style={{ padding: 14, borderRadius: 12, backgroundColor: '#F4F2EE' }} > <Text> Notifications:{' '} {[prefs.push && 'push', prefs.email && 'email'] .filter(Boolean) .join(' + ') || 'off'} </Text> </Pressable> <PickerSheet visible={open} onClose={() => setOpen(false)} title="Notifications" confirmLabel="Save" onConfirm={() => { setPrefs(draft); setOpen(false); }} > <View style={{ padding: 16, gap: 16 }}> {(['push', 'email'] as const).map((k) => ( <View key={k} style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }} > <Text style={{ fontSize: 16, textTransform: 'capitalize' }}> {k} </Text> <Switch value={draft[k]} onValueChange={(v) => setDraft({ ...draft, [k]: v })} /> </View> ))} </View> </PickerSheet> </>
useDraft​
function useDraft<V>(
value: V,
visible: boolean,
options?: { resetKey?: unknown; commit?: (v: V) => void }
): [draft: V, setDraft: (v: V) => void];
Returns a draft copy of value that resets to value each time visible turns true (and whenever resetKey changes). It's the hook the built-in sheets use; use it for your own PickerSheets as shown above.
Inside a PickerField set to presentation="inline" there's no Done button, so setDraft also calls commit with each change. Pass your save function there to support inline: useDraft(prefs, visible, { commit: onSave }).
Props​
Shared props​
All sheets accept these (SheetChromeProps):
| Prop | Type | Default | Description |
|---|---|---|---|
visible | boolean | required | Show / hide. |
onClose | () => void | required | Set visible to false here. |
title | ReactNode | — | Header title. |
confirmLabel | string | 'Done' · 'OK' Material | Confirm button text. |
cancelLabel | string | 'Cancel' | Cancel button text. |
showCancel | boolean | true (false for emoji) | Show the Cancel button. |
presentation | 'auto' | 'bottomSheet' | 'modal' | 'auto' | Bottom sheet or centered modal. auto: modal on wide web windows, bottom sheet elsewhere. |
sheetProps | Partial<BottomSheetProps> | — | Passed to the BottomSheet (height, bottomInset, styles, enableSwipeToClose…). |
theme | DeepPartial<PickerTheme> | — | Theme for the sheet and the picker. |
design | 'ios' | 'material' | 'web' | from PickersProvider | The look to follow, over the provider's: iOS, Material or web. |
DatePickerSheet props​
Shared props, plus every calendar prop (minDate, maxDate, isDateDisabled, markedDates, locale, firstDayOfWeek, styles, renderDay…), plus:
| Prop | Type | Default | Description |
|---|---|---|---|
mode | 'single' | 'range' | 'multiple' | 'ranges' | 'single' | Selection mode. |
value | Date | null · DateRange · Date[] · DateRange[] | — | Current committed value; copied into the draft on open. |
onConfirm | (date: Date) · (range: DateRange) · (dates: Date[]) · (ranges: DateRange[]) => void | required | Called with the draft on Done. |
minRangeDays / maxRangeDays | number | — | Range and ranges modes. |
presets | DateRangePreset[] | — | Range mode: chips that pick a whole range, such as getRangePresets(). |
presetsProps | RangePresetsListProps | — | Range mode: HorizontalList props for the presets row. |
maxSelections | number | — | Multiple mode. |
maxRanges | number | — | Ranges mode. |
display | 'calendar' | 'wheel' | 'calendar' | Wheel is single mode only. |
wheelProps | Partial<DateWheelPickerProps> | — | Extra props for the wheel. |
confirmOnSelect | boolean | false | Single calendar: confirm on tap, no Done button. |
TimePickerSheet props​
Shared props, every TimePicker prop except value/defaultValue/onChange, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
value | Date | now | Copied into the draft on open. |
onConfirm | (date: Date) => void | required | Called with the draft on Done. |
WheelPickerSheet props​
Shared props, every WheelPicker prop except value/defaultValue/onChange, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
value | T | first item | Copied into the draft on open. |
onConfirm | (value: T) => void | required | Called with the draft on Done. |
EmojiPickerSheet props​
Shared props, every EmojiPicker prop, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
closeOnSelect | boolean | true | Close after a pick. |
height | number | 420 | Picker height inside the sheet. |
showCancel | boolean | false | Show a Cancel button. |
PickerSheet props​
Shared props, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | required | Sheet content. |
onConfirm | () => void | — | Shows the confirm button. You close the sheet (call onClose / set visible). |
confirmDisabled | boolean | false | Disable the confirm button. |
presentation | 'auto' | 'bottomSheet' | 'modal' | 'dropdown' | 'inline' | the field's, else 'auto' | Inside a field, it follows the field's presentation. |