Datepicker
A composable date picker with masked input, popup calendar, and support for single, range, and dual-range selection.
Features
- Input with masked entry
- Popup calendar
- Single, range & dual-range modes
- Controlled or uncontrolled
- Focus management
- Headless styling
Installation
npm install @ariaui/datepicker
Examples
Three playgrounds: single date (MDY mask), range with an adjacent time Select, and dual-range (two-pane) stay picker. Calendar styling reuses the same token-backed class maps as the Calendar docs page.
Single date
Uncontrolled single value, MDY mask, and a styled calendar popover.
No preview output yet.
Date range + time
Range mode with ISO-shaped input text; a separate Select demonstrates pairing time with the same row.
No preview output yet.
Dual-range
Hotel-style stay picker: dual-range calendar with shared navigation icons.
No preview output yet.
Framer Motion
Single datepicker content animated with Framer Motion through Content asChild composition.
No preview output yet.
Anatomy
import * as Datepicker from "@ariaui/datepicker";
export default function Example() {
return (
<Datepicker.Root>
<Datepicker.Label />
<Datepicker.Input />
<Datepicker.Trigger />
<Datepicker.Content>
<Datepicker.Calendar>
<Datepicker.CalendarHeader>
<Datepicker.CalendarPrevious />
<Datepicker.CalendarMonth />
<Datepicker.CalendarYear />
<Datepicker.CalendarNext />
</Datepicker.CalendarHeader>
<Datepicker.CalendarBody />
</Datepicker.Calendar>
</Datepicker.Content>
</Datepicker.Root>
);
}
API Reference
Root
State container. Owns open state, value state, visible month, and input masking configuration.
| Prop | Type | Default |
|---|---|---|
mode | "single" | "range" | "dual-range" | "single" |
open | boolean | — |
defaultOpen | boolean | false |
onOpenChange | (open: boolean) => void | — |
value | Date | { start?: Date; end?: Date } | — |
defaultValue | Date | { start?: Date; end?: Date } | — |
onValueChange | (value: DatepickerValue) => void | — |
visibleMonth | Date | — |
defaultVisibleMonth | Date | new Date() |
onVisibleMonthChange | (month: Date) => void | — |
disabled | boolean | false |
readOnly | boolean | false |
closeOnSelect | boolean | true |
inputMask | "mdy" | "iso" | "custom" | — |
parseInput | (text: string, mode: DatepickerMode) => DatepickerValue | — |
formatInput | (value: DatepickerValue, mode: DatepickerMode) => string | — |
Label
Visible and accessible label for the datepicker. Registers the label ID for content labelling.
Trigger
Toggles the picker open and closed. Acts as the fallback positioning reference when input ref is unavailable.
| Attribute | Values |
|---|---|
| aria-haspopup | dialog |
| aria-expanded | "true" when picker is open |
| [data-state] | "open" | "closed" |
Input
Editable input reflecting the formatted date value. Supports masked entry with built-in presets. Commits on Enter and blur.
| Prop | Type | Default |
|---|---|---|
placeholder | string | — |
| Attribute | Values |
|---|---|
| aria-haspopup | dialog |
Content
Floating picker surface. Portals to the body, positions relative to the input, and manages focus on open/close.
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
| Attribute | Values |
|---|---|
| role | dialog |
Calendar
Embedded calendar bound to the datepicker state. Wraps @ariaui/calendar with shared mode, value, and visible month.
| Prop | Type | Default |
|---|---|---|
daysInWeek | Record<string, string> | — |
classNames | object | — |
CalendarHeader
Header row for the embedded calendar containing navigation and month/year labels.
CalendarPrevious
Button to navigate to the previous month.
CalendarMonth
Renders the current visible month label.
CalendarMonthSelect
Datepicker-integrated month selector. Closes before the datepicker content when both need to dismiss.
| Prop | Type | Default |
|---|---|---|
triggerClassName | string | — |
contentClassName | string | — |
optionClassName | string | — |
icon | React.ReactNode | — |
checkIcon | React.ReactNode | — |
contentWrapper | (children: React.ReactNode) => React.ReactNode | — |
| Attribute | Values |
|---|---|
| aria-haspopup | listbox |
CalendarYear
Renders the current visible year label.
CalendarYearSelect
Datepicker-integrated year selector. Closes before the datepicker content when both need to dismiss.
| Prop | Type | Default |
|---|---|---|
years | number[] | current year ± 10 |
triggerClassName | string | — |
contentClassName | string | — |
optionClassName | string | — |
icon | React.ReactNode | — |
checkIcon | React.ReactNode | — |
contentWrapper | (children: React.ReactNode) => React.ReactNode | — |
| Attribute | Values |
|---|---|
| aria-haspopup | listbox |
CalendarNext
Button to navigate to the next month.
CalendarBody
Renders the date grid. In dual-range mode, renders a shared two-pane body.
Keyboard
| Shortcut | Action |
|---|---|
| Enter | Commit the typed input value. Select the focused date in the calendar. |
| Esc | Close the picker and return focus to the trigger. |
| Tab | Move focus between input, trigger, and calendar controls. |
| ↓ | Move to the same day in the next week (inside the calendar grid). |
| ↑ | Move to the same day in the previous week (inside the calendar grid). |
| ← | Move to the previous day (inside the calendar grid). |
| → | Move to the next day (inside the calendar grid). |
| Home | Move to the first day of the current week. |
| End | Move to the last day of the current week. |
Accessibility
The Datepicker component implements the WAI-ARIA Date Picker Dialog pattern:
Triggerexposesaria-haspopup="dialog"andaria-expandedto announce the popup calendar.Inputalso carriesaria-haspopup="dialog"for assistive technology awareness.Contentrenders as adialogrole and is labelled by the sharedLabel.- Focus moves into the popup when it opens and restores to the trigger when it closes.
Escapecloses the popup from any focusable element inside it.- Date-grid keyboard navigation (arrow keys, Home, End) is delegated to the embedded
@ariaui/calendar.