Date Picker
Accessible, unstyled date picker: a segmented DateField plus a popover Calendar.
|
|
| Package |
@vizejs/ui/date-picker |
| Maturity |
stable |
| Own the source |
vize lib pull date-picker |
| Requires |
calendar, context, controllable-state, date-field, id, popover |
| Aliases |
datepicker, date popover, calendar input |
| Covers |
WAI-ARIA APG date picker dialog, React Aria DatePicker, Reka UI DatePicker, HTML input type=date |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { DatePicker, DatePickerRoot, DatePickerCalendar, DatePickerContent, DatePickerField, DatePickerTrigger, DatePickerArrow, DatePickerGrid, DatePickerHeading, DatePickerMonthSelect, DatePickerNext, DatePickerPrev, DatePickerYearSelect } from "@vizejs/ui/date-picker";
Or copy the source into your project with vize lib pull date-picker (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Departure date picker combining a segmented field with a popover calendar. -->
<script setup lang="ts">
import { ref } from "vue";
import {
DatePicker,
DatePickerCalendar,
DatePickerContent,
DatePickerField,
DatePickerTrigger,
type PlainDate,
} from "@vizejs/ui/date-picker";
const departure = ref<PlainDate | null>({ year: 2026, month: 10, day: 24 });
</script>
<template>
<DatePicker v-model="departure" name="departure" locale="en-US">
<DatePickerField aria-label="Departure date">
<DatePickerTrigger aria-label="Choose departure date">Calendar</DatePickerTrigger>
</DatePickerField>
<DatePickerContent aria-label="Departure calendar">
<DatePickerCalendar />
</DatePickerContent>
</DatePicker>
</template>
API
DatePicker
Source: src/families/date-time/date-picker/date-picker-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
PlainDate | null |
undefined |
Controlled date; undefined selects uncontrolled mode and null clears. |
defaultValue |
PlainDate | null |
null |
Initial uncontrolled date. |
open |
boolean |
undefined |
Controlled popover open state; undefined selects uncontrolled mode. |
defaultOpen |
boolean |
false |
Initial uncontrolled open state. |
modal |
boolean |
false |
Make outside content inert and contain focus while open. |
min |
PlainDate | null |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher |
undefined |
Predicate for dates that cannot be selected. |
locale |
string |
undefined |
BCP 47 locale for the field and calendar; defaults to the nearest LocaleProvider. |
dir |
"ltr" | "rtl" |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
today |
PlainDate | null |
undefined |
Explicit current date for the calendar; the SSR-safe way to mark today. |
now |
DateTimeNow |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string |
undefined |
IANA time zone used with now and host clocks. |
disabled |
boolean |
false |
Disable the field, trigger, and calendar. |
readOnly |
boolean |
false |
Lock the value while the calendar stays browsable. |
required |
boolean |
false |
Mark the field as required. |
name |
string |
undefined |
Hidden input name that submits the ISO date with forms. |
closeOnSelect |
boolean |
true |
Close the popover after a calendar selection. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: PlainDate | null] |
Fired when the picker requests a new controlled date. |
change |
[value: PlainDate | null, previous: PlainDate | null, nativeEvent: Event | null] |
Fired after a distinct committed date with the previous date and triggering event. |
update:open |
[value: boolean] |
Fired when the picker requests a new controlled open state. |
open-change |
[value: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct open-state request. |
Slots
| Slot |
Slot props |
Description |
default |
DatePickerSlotState |
Picker composition: DatePickerField, DatePickerTrigger, DatePickerContent, DatePickerCalendar. |
Exposed
| Member |
Type |
Description |
value |
— |
|
open |
— |
|
disabled |
— |
|
readOnly |
— |
|
state |
— |
|
setValue |
— |
|
setOpen |
— |
|
DatePickerRoot
Source: src/families/date-time/date-picker/date-picker-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id; nullish values use a deterministic fallback. |
modelValue |
PlainDate | null |
undefined |
Controlled date; undefined selects uncontrolled mode and null clears. |
defaultValue |
PlainDate | null |
null |
Initial uncontrolled date. |
open |
boolean |
undefined |
Controlled popover open state; undefined selects uncontrolled mode. |
defaultOpen |
boolean |
false |
Initial uncontrolled open state. |
modal |
boolean |
false |
Make outside content inert and contain focus while open. |
min |
PlainDate | null |
undefined |
Earliest selectable date, inclusive. |
max |
PlainDate | null |
undefined |
Latest selectable date, inclusive. |
isDateUnavailable |
DateMatcher |
undefined |
Predicate for dates that cannot be selected. |
locale |
string |
undefined |
BCP 47 locale for the field and calendar; defaults to the nearest LocaleProvider. |
dir |
"ltr" | "rtl" |
undefined |
Text direction; defaults to the nearest LocaleProvider. |
today |
PlainDate | null |
undefined |
Explicit current date for the calendar; the SSR-safe way to mark today. |
now |
DateTimeNow |
undefined |
Injectable clock evaluated during setup on server and client. |
timeZone |
string |
undefined |
IANA time zone used with now and host clocks. |
disabled |
boolean |
false |
Disable the field, trigger, and calendar. |
readOnly |
boolean |
false |
Lock the value while the calendar stays browsable. |
required |
boolean |
false |
Mark the field as required. |
name |
string |
undefined |
Hidden input name that submits the ISO date with forms. |
closeOnSelect |
boolean |
true |
Close the popover after a calendar selection. |
Events
| Event |
Payload |
Description |
update:modelValue |
[value: PlainDate | null] |
Fired when the picker requests a new controlled date. |
change |
[value: PlainDate | null, previous: PlainDate | null, nativeEvent: Event | null] |
Fired after a distinct committed date with the previous date and triggering event. |
update:open |
[value: boolean] |
Fired when the picker requests a new controlled open state. |
open-change |
[value: boolean, previous: boolean, nativeEvent: Event | null] |
Fired after any distinct open-state request. |
Slots
| Slot |
Slot props |
Description |
default |
DatePickerSlotState |
Picker composition: DatePickerField, DatePickerTrigger, DatePickerContent, DatePickerCalendar. |
Exposed
| Member |
Type |
Description |
value |
PlainDate | null |
Committed date. |
open |
boolean |
Whether the calendar popover is open. |
disabled |
boolean |
Whether the picker is disabled. |
readOnly |
boolean |
Whether the value is locked while the calendar can still be browsed. |
state |
PickerOpenState |
Stable open-state token. |
setValue |
(value: PlainDate | null) => boolean |
Request a value; returns whether it differs. |
setOpen |
(value: boolean) => boolean |
Request an open state; returns whether it differs. |
DatePickerCalendar
Source: src/families/date-time/date-picker/date-picker-calendar.vue
Props
| Prop |
Type |
Default |
Description |
numberOfMonths |
number |
1 |
Number of consecutive months rendered. |
pagedNavigation |
boolean |
false |
Move month controls by numberOfMonths. |
fixedWeeks |
boolean |
false |
Always render six week rows per month. |
weekStartsOn |
Weekday |
undefined |
First day of week (0 = Sunday); defaults to the locale preference. |
weekdayFormat |
CalendarWeekdayFormat |
"short" |
Weekday column label width. |
calendar |
string |
undefined |
Intl calendar used for display labels only. |
numberingSystem |
string |
undefined |
Intl numbering system for labels. |
ariaLabel |
string |
undefined |
Accessible name for the calendar group. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarSlotState |
Calendar composition; defaults to a header plus one grid per month. |
DatePickerContent
Source: src/families/date-time/date-picker/date-picker-content.vue
Props
| Prop |
Type |
Default |
Description |
placement |
Placement |
"bottom-start" |
Preferred placement before collision handling. |
initialFocus |
() => HTMLElement | null | undefined |
undefined |
Initial focus target; defaults to the roving calendar day. |
ariaLabel |
string |
undefined |
Accessible dialog name. |
ariaLabelledby |
string |
undefined |
Ids that label the dialog. |
Slots
| Slot |
Slot props |
Description |
default |
Record<string, never> |
Popover body, typically DatePickerCalendar. |
DatePickerField
Source: src/families/date-time/date-picker/date-picker-field.vue
Props
| Prop |
Type |
Default |
Description |
placeholderValue |
PlainDate | null |
undefined |
Date whose segments seed arrow-key stepping from empty; defaults to the picker today or host date. |
placeholders |
FieldSegmentPlaceholders |
{ year: "yyyy", month: "mm", day: "dd" } |
Placeholder text for empty segments. |
emptyText |
string |
"Empty" |
Text announced for empty segments. |
ariaLabel |
string |
undefined |
Accessible name for the segment group. |
ariaLabelledby |
string |
undefined |
Ids that label the segment group. |
ariaDescribedby |
string |
undefined |
Ids that describe the group and every segment. |
ariaErrormessage |
string |
undefined |
Id of the validation message used while invalid. |
ariaInvalid |
boolean |
false |
Force the invalid state. |
Slots
| Slot |
Slot props |
Description |
default |
DateFieldSlotState |
Extra content after the segments, typically DatePickerTrigger. Receives field state. |
segment |
FieldSegmentState |
Segment content. Receives the segment state; defaults to its text. |
Exposed
| Member |
Type |
Description |
focus |
— |
|
DatePickerTrigger
Source: src/families/overlays/popover/popover-trigger.vue
Props
| Prop |
Type |
Default |
Description |
type |
"button" | "reset" | "submit" |
"button" |
Native button submission behavior. |
disabled |
boolean |
false |
Remove the trigger from activation and sequential keyboard focus. |
ariaLabel |
string |
undefined |
Accessible name when no visible label supplies one. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the trigger toggles the popover. Call preventDefault() to keep state unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
PopoverSlotState |
Trigger contents. Receives the current Popover state and trigger availability. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
DatePickerArrow
Source: src/families/overlays/popover/popover-arrow.vue
Slots
| Slot |
Slot props |
Description |
default |
PopoverArrowSlotState |
Decorative arrow contents. Receives coordinates for optional custom drawing. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
x |
— |
|
y |
— |
|
DatePickerGrid
Source: src/families/date-time/calendar/calendar-grid.vue
Props
| Prop |
Type |
Default |
Description |
monthIndex |
number |
0 |
Zero-based index of the visible month this grid renders. |
showWeekNumbers |
boolean |
false |
Render a leading row header with the ISO 8601 week number of each row. |
weekNumberLabel |
string |
"Wk" |
Visible week-number column header. |
weekNumberHeader |
string |
"Week" |
Full week-number column header used as abbr. |
Slots
| Slot |
Slot props |
Description |
weekday |
CalendarWeekdaySlotState |
Weekday column header content. Receives the weekday, its labels, and column index. |
weekNumber |
CalendarWeekNumberSlotState |
Week-number row header content. Receives the ISO week and the row's days. |
day |
CalendarDayState |
Day button content. Receives the complete day state; defaults to the localized day number. |
DatePickerHeading
Source: src/families/date-time/calendar/calendar-heading.vue
Slots
| Slot |
Slot props |
Description |
default |
CalendarHeadingSlotState |
Heading content. Receives the localized heading and visible months. |
DatePickerMonthSelect
Source: src/families/date-time/calendar/calendar-month-select.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
"Month" |
Accessible name of the native select. |
Events
| Event |
Payload |
Description |
change |
[month: number, nativeEvent: Event] |
Fired after the user picks a month, with the ISO month number. |
DatePickerNext
Source: src/families/date-time/calendar/calendar-next.vue
Props
| Prop |
Type |
Default |
Description |
unit |
CalendarNavigationUnit |
"month" |
Whether the control pages by month (or numberOfMonths when paged) or by year. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Next month" or "Next year". |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before navigation. Call preventDefault() to keep the view unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarNavigationSlotState |
Control content. Receives the unit and whether navigation is possible. |
DatePickerPrev
Source: src/families/date-time/calendar/calendar-prev.vue
Props
| Prop |
Type |
Default |
Description |
unit |
CalendarNavigationUnit |
"month" |
Whether the control pages by month (or numberOfMonths when paged) or by year. |
ariaLabel |
string |
undefined |
Accessible name; defaults to "Previous month" or "Previous year". |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before navigation. Call preventDefault() to keep the view unchanged. |
Slots
| Slot |
Slot props |
Description |
default |
CalendarNavigationSlotState |
Control content. Receives the unit and whether navigation is possible. |
DatePickerYearSelect
Source: src/families/date-time/calendar/calendar-year-select.vue
Props
| Prop |
Type |
Default |
Description |
from |
number |
undefined |
First listed year; defaults to the min year or ten years before the view. |
to |
number |
undefined |
Last listed year; defaults to the max year or ten years after the view. |
ariaLabel |
string |
"Year" |
Accessible name of the native select. |
Events
| Event |
Payload |
Description |
change |
[year: number, nativeEvent: Event] |
Fired after the user picks a year. |
Behavior
Normative state x input -> outcome table for the DatePicker compound
(@vizejs/ui/date-picker): date-picker-root.vue, date-picker-field.vue,
date-picker-content.vue, and date-picker-calendar.vue, with
DatePickerTrigger re-exporting PopoverTrigger. The root wraps PopoverRoot, so
dismissal, focus containment, and positioning follow popover.behavior.md;
field and calendar rules follow date-field.behavior.md and
calendar.behavior.md. Every row is proven by the named test.
| # |
State |
Input |
Outcome |
Proven by |
| P1 |
closed |
trigger activation |
the trigger (aria-haspopup="dialog") opens the calendar dialog, emits update:open and open-change, and moves focus to the selected (or today) day |
renders a date field with a trigger that opens a calendar dialog focused on the value |
| P2 |
open |
day activation |
the value commits (update:modelValue, change), the popover closes, focus returns to the trigger, and the field segments update |
selecting a day commits the value, closes the popover, and restores focus to the trigger |
| P3 |
open |
field typing / Escape |
a value typed in the field scrolls the open calendar to it; Escape dismisses |
typing in the field moves the open calendar and keyboard Escape dismisses |
| P4 |
closeOnSelect=false / read-only |
day activation |
the popover stays open; read-only pickers never change the value |
closeOnSelect=false keeps the popover open and read-only blocks value changes |
| P5 |
disabled / imperative |
render / expose |
disabled pickers disable the trigger and field; setOpen and setValue are exposed; min reaches the calendar |
disabled pickers cannot open and expose imperative value and open control |
| P6 |
SSR/hydration |
closed and open requests |
byte-identical markup in both states; an open picker hydrates silently |
renders byte-identical closed and open picker markup across SSR requests, hydrates an open picker without mismatches |
DatePickerContent forwards placement, initialFocus, ariaLabel, and
ariaLabelledby; every other PopoverContent prop (for example portalDisabled
or offset) falls through as an attribute. Root data: data-vize-ui="date-picker",
data-state (open/closed), data-value, data-disabled, data-readonly.
Pass today or now to the root for SSR-stable calendars.