Skip to main content
Version: v0.30.x

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.

Opening a sheet from a form field?

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​

Platform
presentation
mode
display
<DatePickerSheet
visible={open}
onClose={() => setOpen(false)}
value={value}
onConfirm={setValue}
/>

How drafts work​

  1. When the sheet opens, it copies value into a draft.
  2. The picker inside edits only the draft.
  3. Done calls onConfirm(draft) and then onClose().
  4. 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.

Loading…
<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 start and end in 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 at value or today. locale, minDate and maxDate are forwarded; pass other wheel props via wheelProps.
  • 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.

Loading…
<>
  <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.

Loading…
<>
  <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.

Loading…
<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.

Loading…
<>
  <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):

PropTypeDefaultDescription
visiblebooleanrequiredShow / hide.
onClose() => voidrequiredSet visible to false here.
titleReactNode—Header title.
confirmLabelstring'Done' · 'OK' MaterialConfirm button text.
cancelLabelstring'Cancel'Cancel button text.
showCancelbooleantrue (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.
sheetPropsPartial<BottomSheetProps>—Passed to the BottomSheet (height, bottomInset, styles, enableSwipeToClose…).
themeDeepPartial<PickerTheme>—Theme for the sheet and the picker.
design'ios' | 'material' | 'web'from PickersProviderThe 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:

PropTypeDefaultDescription
mode'single' | 'range' | 'multiple' | 'ranges''single'Selection mode.
valueDate | null · DateRange · Date[] · DateRange[]—Current committed value; copied into the draft on open.
onConfirm(date: Date) · (range: DateRange) · (dates: Date[]) · (ranges: DateRange[]) => voidrequiredCalled with the draft on Done.
minRangeDays / maxRangeDaysnumber—Range and ranges modes.
presetsDateRangePreset[]—Range mode: chips that pick a whole range, such as getRangePresets().
presetsPropsRangePresetsListProps—Range mode: HorizontalList props for the presets row.
maxSelectionsnumber—Multiple mode.
maxRangesnumber—Ranges mode.
display'calendar' | 'wheel''calendar'Wheel is single mode only.
wheelPropsPartial<DateWheelPickerProps>—Extra props for the wheel.
confirmOnSelectbooleanfalseSingle calendar: confirm on tap, no Done button.

TimePickerSheet props​

Shared props, every TimePicker prop except value/defaultValue/onChange, plus:

PropTypeDefaultDescription
valueDatenowCopied into the draft on open.
onConfirm(date: Date) => voidrequiredCalled with the draft on Done.

WheelPickerSheet props​

Shared props, every WheelPicker prop except value/defaultValue/onChange, plus:

PropTypeDefaultDescription
valueTfirst itemCopied into the draft on open.
onConfirm(value: T) => voidrequiredCalled with the draft on Done.

EmojiPickerSheet props​

Shared props, every EmojiPicker prop, plus:

PropTypeDefaultDescription
closeOnSelectbooleantrueClose after a pick.
heightnumber420Picker height inside the sheet.
showCancelbooleanfalseShow a Cancel button.

PickerSheet props​

Shared props, plus:

PropTypeDefaultDescription
childrenReactNoderequiredSheet content.
onConfirm() => void—Shows the confirm button. You close the sheet (call onClose / set visible).
confirmDisabledbooleanfalseDisable the confirm button.
presentation'auto' | 'bottomSheet' | 'modal' | 'dropdown' | 'inline'the field's, else 'auto'Inside a field, it follows the field's presentation.