DatePicker
A date and date-range input with a dropdown calendar, optional shortcuts panel, and full i18n support.
Overview
DatePicker is a controlled input that opens a calendar dropdown for selecting a single date or a date range. Key features:
- Single or range selection — controlled by
isRange. Range mode uses a{ from, to }value object. - Single or dual calendar —
mode="single"shows one month at a time;mode="dual"shows two months side-by-side for easier range picking. - Shortcuts — pass a
shortcutsarray for quick-select options (e.g. "Today", "This week"). - Localization — driven by a BCP-47
localecode; any label can be overridden vialocaleText. - Form-compatible events —
onChangeandonBlurmimic the React input event shape (e.target.name,e.target.value,e.target.type = 'date').
Import
import { DatePicker } from '@unflow.io/ui/components/DatePicker';Props
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Required. Input name used in change/blur events |
value | DatePickerValue | — | Controlled value — Date | null for single; { from, to } for range |
isRange | boolean | — | Enables range selection mode |
mode | 'single' | 'dual' | — | Number of calendar months shown in the dropdown |
label | string | — | Visible label above the input |
hint | string | — | Helper text below the label |
autoApply | boolean | — | Close and apply on date click without an explicit Apply button |
shortcuts | DatePickerShortcutItem[] | — | Quick-select date/range options |
hideDaysOutsideMonth | boolean | — | Hide days from adjacent months in the calendar grid |
minYear | number | 1970 | Earliest selectable year in the year dropdown |
maxYear | number | current + 10 | Latest selectable year in the year dropdown |
locale | string | active i18n language | BCP-47 code controlling month names, weekday labels, format, and week start |
weekStartsOn | 0–6 | locale default | First day of the week (0 = Sunday, 1 = Monday, …, 6 = Saturday) |
localeText | DatePickerLocaleText | — | Inline string overrides — highest precedence over i18n and Intl API |
onChange | (e: DatePickerChangeEvent) => void | — | Fires when the selected value changes |
onBlur | (e: DatePickerBlurEvent) => void | — | Fires when the input loses focus |
onCancel | () => void | — | Fires when the user cancels the dropdown |
disabled | boolean | — | Disables the input |
required | boolean | — | Marks the input as required |
Value types
The value and onChange event type depend on whether isRange is set:
| Mode | Value shape | Example |
|---|---|---|
Single (isRange={false}) | Date | null | undefined | new Date('2024-06-15') |
Range (isRange={true}) | { from: Date | null, to: Date | null } | null | { from: new Date(...), to: new Date(...) } |
Important: Always pass
Dateobjects. Strings and timestamps (numbers) are rejected at runtime.
Single date
'use client';
import { useState } from 'react';
import { DatePicker } from '@unflow.io/ui/components/DatePicker';
export function Example() {
const [value, setValue] = useState<Date | null>(null);
return (
<DatePicker
name="date"
label="Select a date"
isRange={false}
mode="single"
value={value}
onChange={(e) => setValue(e.target.value as Date | null)}
/>
);
}Date range
'use client';
import { useState } from 'react';
import { DatePicker } from '@unflow.io/ui/components/DatePicker';
type RangeValue = { from: Date | null; to: Date | null } | null;
export function Example() {
const [value, setValue] = useState<RangeValue>(null);
return (
<DatePicker
name="dateRange"
label="Select a date range"
isRange
mode="dual"
value={value}
onChange={(e) => setValue(e.target.value as RangeValue)}
/>
);
}Range selection works in two steps: the first click sets from; the second click sets to. Clicking a date before the current from resets from and starts the selection over.
With shortcuts
const now = new Date();
const shortcuts = [
{ label: 'Today', value: { from: startOfDay(now), to: endOfDay(now) } },
{
label: 'This week',
value: {
from: startOfDay(new Date(now.getFullYear(), now.getMonth(), now.getDate() - now.getDay())),
to: endOfDay(new Date(now.getFullYear(), now.getMonth(), now.getDate() - now.getDay() + 6)),
},
},
];
<DatePicker
name="dateRange"
label="Select a period"
isRange
mode="dual"
value={value}
shortcuts={shortcuts}
onChange={(e) => setValue(e.target.value as RangeValue)}
/>Each shortcut value must match the picker's mode: a plain Date when isRange={false}, or a { from, to } object when isRange={true}.
Localization
<DatePicker
name="date"
label="Selecionar data"
isRange={false}
mode="single"
locale="pt"
weekStartsOn={1}
localeText={{ apply: 'Aplicar', cancel: 'Cancelar', reset: 'Limpar' }}
value={value}
onChange={(e) => setValue(e.target.value as Date | null)}
/>Locale resolution priority (highest → lowest):
localeTextprop — inline overrides for any label- i18n translations — registered translations for the resolved locale
- Intl API fallback — automatic month/weekday names for unregistered locales
weekStartsOn accepts 0 (Sunday) through 6 (Saturday) and overrides the locale's CLDR default.
All overridable labels via localeText:
| Key | Description |
|---|---|
months | 12-element array of month names (index 0 = January) |
weekdays | 7-element array of short weekday labels (index 0 = Sunday) |
today | "Today" label |
date | Single-date input label |
startDate | Range "from" field label |
endDate | Range "to" field label |
apply | Apply button label |
cancel | Cancel button label |
reset | Reset button label |
previousMonth | Previous month button aria-label |
nextMonth | Next month button aria-label |
shortcuts | Shortcuts panel heading |
toggleOn | Calendar open button aria-label |
toggleOff | Calendar close button aria-label |
format | Date format string, e.g. 'MM/dd/yyyy' |
Events
Both onChange and onBlur receive a synthetic event that mirrors the React input event shape:
type DatePickerChangeEvent = {
type: 'change';
target: { name: string; value: DatePickerValue; type: 'date' };
currentTarget: { name: string; value: DatePickerValue };
nativeEvent?: Event;
preventDefault: () => void;
stopPropagation: () => void;
persist: () => void;
};This makes DatePicker compatible with form libraries like React Hook Form that read e.target.name and e.target.value.
Customization (slotProps)
| Slot | Props | Description |
|---|---|---|
calendarIcon | ReactNode (slot) | Icon inside the calendar toggle button |
separator | ReactNode (slot) | Element between the from/to inputs in range mode |
calendarToggler | Omit<IIconButton, 'onClick' | 'aria-label' | 'type' | 'icon'> | Calendar toggle button props |
inputContainer | BaseHTMLProps<HTMLDivElement> | Outer wrapper element |
inputLabel | Omit<IInputLabel, ...> | Label/hint component props |
datePickerDropdown | Omit<IDatePickerDropdown, ...> | Advanced dropdown customization |
Accessibility
- The calendar toggle button uses
localeText.toggleOn/localeText.toggleOffas itsaria-label— always provide translated values when customizing locale. - Inside the calendar grid, arrow keys move focus between days; Enter and Space select the focused day.
- The month/year selectors are keyboard-accessible dropdowns.
- Provide an accessible
labeloraria-labelon the component so screen readers can identify the input field.