Forms
Every picker is a controlled component, so it plugs into any form library. In a form you usually want a field that opens the picker: SelectPicker and the picker fields (DatePickerField, TimePickerField, DateTimePickerField, WheelPickerField, EmojiPickerField) render one, and report the value through onChange (select) or onConfirm (fields). Their onClose is the natural place for onBlur.
React Hook Form
import { useForm, Controller } from 'react-hook-form';
import { Text, View, Button } from 'react-native';
import { SelectPicker, DatePickerField, TimePickerField } from '@elik56a/react-native-pickers';
import type { DateRange } from '@elik56a/react-native-pickers';
type FormValues = {
country: string | null;
tags: string[];
date: Date | null;
time: Date;
stay: DateRange;
};
export function BookingForm() {
const { control, handleSubmit, formState: { errors } } = useForm<FormValues>({
defaultValues: { country: null, tags: [], date: null, time: new Date(), stay: { start: null, end: null } },
});
return (
<View style={{ gap: 16 }}>
<Controller
control={control}
name="country"
rules={{ required: 'Pick a country' }}
render={({ field }) => (
<SelectPicker label="Country" options={countries} value={field.value} onChange={field.onChange} onClose={field.onBlur} />
)}
/>
{errors.country && <Text style={{ color: 'crimson' }}>{errors.country.message}</Text>}
<Controller
control={control}
name="tags"
rules={{ validate: (v) => v.length > 0 || 'Pick at least one tag' }}
render={({ field }) => <SelectPicker multiple label="Tags" options={tags} value={field.value} onChange={field.onChange} />}
/>
<Controller
control={control}
name="stay"
rules={{ validate: (r) => (!!r.start && !!r.end) || 'Choose check-in and check-out' }}
render={({ field }) => (
<DatePickerField
label="Stay"
placeholder="Add dates"
mode="range"
value={field.value}
onConfirm={field.onChange}
onClose={field.onBlur}
minDate={new Date()}
/>
)}
/>
{errors.stay && <Text style={{ color: 'crimson' }}>{errors.stay.message}</Text>}
<Controller
control={control}
name="time"
render={({ field }) => <TimePickerField label="Arrival" value={field.value} onConfirm={field.onChange} onClose={field.onBlur} />}
/>
<Button title="Submit" onPress={handleSubmit(console.log)} />
</View>
);
}
field.onChange receives the first argument, which is the value, so extra arguments such as the option from SelectPicker are ignored.
Optional values
A field with clearable shows a × button while it has a value. Reset the form value in onClear:
<Controller
control={control}
name="date"
render={({ field }) => (
<DatePickerField label="Due date" value={field.value} onConfirm={field.onChange} clearable onClear={() => field.onChange(null)} />
)}
/>
Inline pickers
The inline pickers (DatePicker, TimePicker, WheelPicker…) take value + onChange the same way, for a picker that's always on screen:
<Controller control={control} name="time" render={({ field }) => <TimePicker value={field.value} onChange={field.onChange} />} />
Formik
<Formik initialValues={{ birthday: new Date(2000, 0, 1), size: 'M' }} onSubmit={save}>
{({ values, setFieldValue }) => (
<>
<DatePickerField label="Birthday" display="wheel" value={values.birthday} onConfirm={(d) => setFieldValue('birthday', d)} />
<WheelPickerField label="Size" items={['S', 'M', 'L']} value={values.size} onConfirm={(v) => setFieldValue('size', v)} />
</>
)}
</Formik>
Sending dates to an API
Pickers work with local Date objects. Serialize them for your API deliberately:
import { toDateKey } from '@elik56a/react-native-pickers';
// A calendar day (no time zone): '2026-10-14'
const day = toDateKey(date);
// An instant: '2026-10-14T07:30:00.000Z'
const instant = time.toISOString();
// Parse a 'YYYY-MM-DD' back to a local date (don't use new Date('2026-10-14'), which parses as UTC)
const [y, m, d] = '2026-10-14'.split('-').map(Number);
const local = new Date(y, m - 1, d);
date.toISOString().slice(0, 10) converts to UTC first, and can give yesterday or tomorrow depending on the user's time zone. Use toDateKey for calendar days.
Zod schema
import { z } from 'zod';
const schema = z.object({
stay: z
.object({ start: z.date().nullable(), end: z.date().nullable() })
.refine((r) => r.start && r.end, 'Choose both dates'),
country: z.string({ required_error: 'Pick a country' }),
});