Forms
Every picker is a controlled component with value + onChange, so it plugs into any form library.
React Hook Form
import { useForm, Controller } from 'react-hook-form';
import { Text, View, Button } from 'react-native';
import { SelectPicker, DatePicker, TimePicker } 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 }) => <DatePicker mode="range" value={field.value} onChange={field.onChange} minDate={new Date()} />}
/>
<Controller control={control} name="time" render={({ field }) => <TimePicker value={field.value} onChange={field.onChange} />} />
<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.
With a sheet
Open the sheet from a field and commit on Done:
<Controller
control={control}
name="date"
render={({ field }) => (
<>
<Pressable onPress={() => setOpen(true)}>
<Text>{field.value ? formatDate(field.value) : 'Select a date'}</Text>
</Pressable>
<DatePickerSheet
visible={open}
onClose={() => {
setOpen(false);
field.onBlur();
}}
value={field.value}
onConfirm={field.onChange}
/>
</>
)}
/>
Formik
<Formik initialValues={{ birthday: new Date(2000, 0, 1), size: 'M' }} onSubmit={save}>
{({ values, setFieldValue }) => (
<>
<DatePicker display="wheel" value={values.birthday} onChange={(d) => setFieldValue('birthday', d)} />
<WheelPicker items={['S', 'M', 'L']} value={values.size} onChange={(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);
Day keys vs ISO strings
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' }),
});