Forms
Native forms
Section titled “Native forms”Give a picker a name and it renders one visually hidden input with the canonical value. Ranges use startName and endName and render two.
<form action="/search"> <DateRangePicker startName="from" endName="to" /> <button type="submit">Search</button></form>// submits ?from=2026-10-13&to=2026-10-17required,disabled,readOnlyandformbehave as on native inputs. A required picker blocks submission while empty, and the browser’s message points at the first segment. For ranges, the end is required only whenisOpenEndAllowedis off.invalidmarks the picker invalid (aria-invalid,data-invalid) when your own validation fails.- The hidden input is an off-screen
type="text"input rather thantype="hidden", because browsers skip constraint validation on hidden inputs.
Form reset
Section titled “Form reset”A form reset returns an uncontrolled picker to its defaultValue and calls onValueChange once. Reset needs the hidden input, so give the picker a name (startName and endName for ranges). Controlled pickers ignore reset: reset your own state instead.
react-hook-form
Section titled “react-hook-form”Connect a picker with Controller. A cleared picker sends null, so required works without extra code.
import { DatePicker } from '@lunisolar-picker/react/pickers';import { Controller, useForm } from 'react-hook-form';
export function BirthDateForm() { const { control, handleSubmit } = useForm<{ birthDate: string | null }>({ defaultValues: { birthDate: null } }); return ( <form noValidate onSubmit={handleSubmit((values) => console.log(values))}> <Controller name="birthDate" control={control} rules={{ required: 'Enter your birth date' }} render={({ field, fieldState }) => ( <> <DatePicker ref={field.ref} name={field.name} value={field.value} onValueChange={(value) => field.onChange(value)} onBlur={field.onBlur} required invalid={fieldState.invalid} /> {fieldState.error && <p role="alert">{fieldState.error.message}</p>} </> )} /> <button type="submit">Submit</button> </form> );}reffocuses the first segment (or your trigger withrenderTrigger), so react-hook-form can move focus to the first invalid field.onBlurfires once when focus leaves the whole picker, not on every segment.- If you also pass
requiredto the picker, putnoValidateon the form. Otherwise the browser’s own validation stops the submit before react-hook-form can show its message. - A controlled
DateRangePickerorDateRangeFieldmust pass everyonValueChangevalue back asvalue, including a range whose end is stillnull.
Validating constraints yourself
Section titled “Validating constraints yourself”onValueChange reports why a value breaks the picker’s constraints. You can run the same checks anywhere, for example in a zod refinement or on the server:
import { validateDate, validateRange } from '@lunisolar-picker/core';
validateDate('2031-01-01', { max: '2030-12-31' });// { isValid: false, reasons: ['after-max'], ignoredInputs: [] }
validateRange({ start: '2026-10-01', end: '2026-12-31' }, { maxDays: 60 });// { isValid: false, reasons: ['range-too-long'], ignoredInputs: [] }ignoredInputs lists min or max values that could not be read, so a typo in a constraint is not silently ignored.