Skip to main content
Version: v0.6.x

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' }),
});