Skip to main content
Version: v0.30.x

Picker fields

In a form, a picker usually isn't on screen. The user taps a field, picks in a bottom sheet (a dropdown under the field on the web), and sees the value in the field once it closes. Each picker has a field that does this, with the same look as SelectPicker's field:

FieldOpensShows
DatePickerFieldDatePickerSheetOct 5, 2026, a range Oct 5, 2026 – Oct 9, 2026 or a list of dates
TimePickerFieldTimePickerSheet9:30 AM or 09:30, following format and locale
TimeRangePickerFieldTimeRangePickerSheet9:00 AM – 5:00 PM, with +1 when it ends the next day
DateTimePickerFieldDateTimePickerSheetOct 26, 2026, 9:30 AM
DurationPickerFieldDurationPickerSheet1 h 30 min
MonthPickerFieldMonthPickerSheetOctober 2026
ColorPickerFieldColorPickerSheetA swatch and #E4572E
WheelPickerFieldWheelPickerSheetThe selected item's label
EmojiPickerFieldEmojiPickerSheetvalue, the emoji you pass
PickerFieldAny sheet you renderdisplayValue
import { DatePickerField, TimePickerField, DateTimePickerField, WheelPickerField, EmojiPickerField, PickerField } from '@elik56a/react-native-pickers';
Loading…
<View style={{ gap: 14 }}>
  <DatePickerField
    label="Stay"
    placeholder="Add dates"
    mode="range"
    value={stay}
    onConfirm={setStay}
    minDate={new Date()}
  />
  <TimePickerField
    label="Arrival time"
    placeholder="Choose a time"
    value={arrival}
    onConfirm={setArrival}
    minuteInterval={15}
  />
  <WheelPickerField
    label="Guests"
    placeholder="How many?"
    items={guests}
    value={guestCount}
    onConfirm={setGuestCount}
  />
  <SelectPicker
    label="Room"
    placeholder="Choose a room"
    options={rooms}
    value={room}
    onChange={setRoom}
  />
  <DateTimePickerField
    label="Reminder"
    placeholder="Set a reminder"
    value={reminder}
    onConfirm={setReminder}
    clearable
    onClear={() => setReminder(undefined)}
  />
  <EmojiPickerField
    label="Mood"
    placeholder="Pick an emoji"
    value={mood}
    onSelect={setMood}
  />
</View>

Each field takes the props of the sheet it opens, without visible, plus the field props. The field owns the open state; you keep the value. As with the sheets, onConfirm fires only on Done, so your state changes only when the user confirms (except inline, which has no Done and commits each change). On the web they open as dropdowns; switch presentation in the panel to see the same form with bottom sheets or modals.

Try the props​

Platform
mode
display
presentation
error
<DatePickerField
label="Dates"
placeholder="Pick dates"
value={value}
onConfirm={setValue}
/>

presentation sets where the picker opens:

presentationOpens
'auto' (default)A dropdown on the web, a bottom sheet on native.
'dropdown'Under the field, like a menu, or above it when there's more room there. Cancel and Done sit below the picker; a tap outside cancels. It's at least 320 px wide, so a calendar fits under a narrow field. It opens on the side with room for the whole picker; in a window too short for it, the dropdown scrolls.
'bottomSheet'A bottom sheet with the title and buttons in its header.
'modal'A centered dialog.
'inline'In place, under the field: the picker expands down, pushing the content below it, and folds back up on a second tap on the field. There's no Cancel or Done: each change goes to onConfirm (or onSelect) right away, like the iOS Settings and Clock apps. A date range commits once both ends are picked.
<DatePickerField label="Stay" mode="range" value={stay} onConfirm={setStay} />
<DatePickerField label="Stay" mode="range" value={stay} onConfirm={setStay} presentation="dropdown" />
<DurationPickerField label="Snooze" value={snooze} onConfirm={setSnooze} presentation="inline" />
Loading…
<View style={{ gap: 16 }}>
  <TimePickerField label="Alarm" value={alarm} onConfirm={setAlarm} presentation="inline" />
  <DurationPickerField label="Snooze" value={snooze} onConfirm={setSnooze} showHours={false} presentation="inline" />
</View>

sheetProps and title apply to the bottom sheet and the modal; the dropdown and the inline picker have no header. With renderTrigger, open() toggles an inline picker, so your trigger collapses it too. SelectPicker takes the same values and the same 'auto' default.

What the field shows​

Dates and times are formatted with Intl for the field's locale (the device locale by default). TimePickerField, TimeRangePickerField and DateTimePickerField follow format ('12h', '24h' or 'auto') and showSeconds.

For your own wording, pass displayValue:

<DatePickerField
label="Delivery"
value={day}
onConfirm={setDay}
displayValue={day ? formatDate(day, undefined, { weekday: 'long', day: 'numeric', month: 'long' }) : undefined}
/>

When displayValue is empty and there's no value, the field shows placeholder (default 'Select…'). In the Material design, a field with a label shows the label there instead, and the placeholder only while it's open.

Label and sheet title​

label is drawn above the field (in the Material design, inside it until there's a value, then on its top border), used as its accessibility label and, unless you set title, as the sheet's title (a bottom sheet or modal; the dropdown has no title).

<TimePickerField label="Alarm" title="Wake me up at" value={alarm} onConfirm={setAlarm} />

Clearing​

clearable shows a × button while the field has a value. Reset your state in onClear:

<DatePickerField label="Due" value={due} onConfirm={setDue} clearable onClear={() => setDue(null)} />

Errors and helper text​

error shows the field as invalid: a red border and label, and the message under the field when it's a string. helperText is the line under the field while there's no error message. required adds an asterisk to the label. The field doesn't check the value itself: your form decides what's invalid and when to say so, so set error from React Hook Form, Formik or Zod.

Loading…
<View style={{ gap: 14 }}>
  <DatePickerField
    label="Check-in"
    required
    helperText="You can change it until the day before"
    error={submitted && !day && 'Choose a check-in date'}
    value={day}
    onConfirm={setDay}
    minDate={new Date()}
  />
  <SelectPicker
    label="Room"
    required
    error={submitted && !room && 'Choose a room'}
    options={rooms}
    value={room}
    onChange={setRoom}
  />
  <DemoButton title="Book" onPress={() => setSubmitted(true)} />
</View>

error={true} marks the field without a message, and shows helperText in red. SelectPicker, CountryPicker, TimeZonePicker PhoneField and AmountField take the same three props.

Custom trigger​

renderTrigger replaces the field box, and gets { open, displayValue, disabled, error }. The label (except Material's, which is drawn inside the field box) and the line under the field still render around it, and the sheet still opens from the field; use error to show your trigger as invalid.

Loading…
<EmojiPickerField
  label="Project icon"
  value={icon}
  onSelect={setIcon}
  renderTrigger={({ open, displayValue }) => (
    <Pressable
      onPress={open}
      accessibilityRole="button"
      accessibilityLabel="Change project icon"
      style={{
        width: 64,
        height: 64,
        borderRadius: 16,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#F4F2EE',
      }}
    >
      <Text style={{ fontSize: 34 }}>{displayValue}</Text>
    </Pressable>
  )}
/>

renderChevron swaps only the chevron icon and keeps its rotation, as in SelectPicker.

Your own sheet: PickerField​

PickerField is the field without a picker. Its child is a function that gets { visible, onClose } for the sheet you render, so any BottomSheet or PickerSheet can open from a field. A PickerSheet inside follows the field's presentation, so it opens as a dropdown on the web; a presentation set on the PickerSheet itself wins.

Loading…
<PickerField label="Notifications" displayValue={summary}>
  {(sheet) => (
    <NotificationsSheet {...sheet} prefs={prefs} onSave={setPrefs} />
  )}
</PickerField>

Styling​

style styles the wrapper (label and field). styles takes the field's slots and the slots of the picker inside the sheet, so one object styles both:

<DatePickerField
label="Check-in"
value={day}
onConfirm={setDay}
styles={{
trigger: { borderRadius: 999 },
triggerText: { fontWeight: '600' },
selectedDay: { borderRadius: 8 },
}}
/>
SlotElement
labelThe label: above the field, or the floating label in the Material design
triggerThe field box
triggerTextThe value
placeholderPlaceholder text
chevronThe drawn chevron (a bordered View: borderColor sets its color, width / height its size); in the Material design, a triangle colored by borderTopColor
clear× button text
helperTextThe line under the field: helper text or the error message

The theme (theme prop or PickersProvider) applies to the field and the sheet.

Accessibility​

The field is a button labelled with label (or the placeholder), with the current value as its hint and an expanded state while the picker is open. With required, the label ends in , required; with error, the message follows the value in the hint (Invalid for error={true}), and the line under the field is a live region, so screen readers announce an error as it appears. The dropdown closes on a tap outside it and on the Android back button. The × button is labelled Clear.

Props​

Every field takes these, plus its sheet's props without visible: value, onConfirm (onSelect for emoji), title, confirmLabel, sheetProps, the picker's own props (mode, minDate, locale, items, minuteInterval…). The picker's own style and testID aren't available on a field; style and testID belong to the field.

PropTypeDefaultDescription
labelstring—Label above the field (floating inside it in Material). Also its accessibility label, and the sheet title unless title is set.
placeholderstring'Select…'Shown while there's no value.
displayValuestringthe value, formatted for localeText shown in the field.
disabledbooleanfalseThe field doesn't open.
clearablebooleanfalseShow a × button while there's a value.
onClear() => void—Called by ×. Reset your value here.
errorstring | boolean—Shows the field as invalid, with the message under it when it's a string. See Errors and helper text.
helperTextstring—Shown under the field, replaced by the error message.
requiredbooleanfalseAdds an asterisk to the label and tells screen readers the field is required. The value isn't checked.
renderTrigger(info: { open, displayValue, disabled, error }) => ReactNode—Replace the field box.
renderChevron(info: { open, theme }) => ReactNode—Custom chevron icon. It still rotates on open and close.
presentation'auto' | 'dropdown' | 'bottomSheet' | 'modal' | 'inline''auto'Where the picker opens: a dropdown on the web, a bottom sheet on native. See Dropdown, bottom sheet, modal or inline.
onOpen / onClose() => void—The picker opened / closed (Done, Cancel or dismiss).
styleStyleProp<ViewStyle>—Wrapper style (label + field).
stylesSlotStyles<PickerFieldSlot | …>—Field slots plus the picker's slots.
themeDeepPartial<PickerTheme>—Theme for the field and the sheet.
design'ios' | 'material' | 'web'from PickersProviderThe look to follow, over the provider's: iOS, Material or web. Passed on to the sheet it opens.
testIDstring—Test ID on the field box.

EmojiPickerField also takes value?: string, the emoji to show. PickerField takes the props above (no sheet props), children: (sheet: { visible, onClose }) => ReactNode, and icon, a node shown before the value such as a colour swatch or a flag.

Types​

import type {
DatePickerFieldProps,
TimePickerFieldProps,
TimeRangePickerFieldProps,
DateTimePickerFieldProps,
DurationPickerFieldProps,
MonthPickerFieldProps,
ColorPickerFieldProps,
WheelPickerFieldProps,
EmojiPickerFieldProps,
PickerFieldProps,
PickerFieldBaseProps,
PickerFieldSlot,
PickerFieldTriggerInfo,
FieldPresentation,
FieldStatusProps,
FieldError,
WithPickerField,
} from '@elik56a/react-native-pickers';

WithPickerField<SheetProps, Slot> turns a sheet's props into a field's, for wrapping your own sheet the same way. FieldStatusProps is { error, helperText, required }, for your own fields.