Date Field
Accessible, unstyled segmented date input whose segment order follows the locale.
| Package | @vizejs/ui/date-field |
| Maturity | stable |
| Own the source | vize lib pull date-field |
| Requires | controllable-state, id, locale |
| Aliases | date input, segmented date, date segments, masked date |
| Covers | WAI-ARIA spinbutton, React Aria DateField, Reka UI DateField, 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 { DateField } from "@vizejs/ui/date-field";
Or copy the source into your project with vize lib pull date-field (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
<!-- Segmented birthday input whose day, month, and year order follows the locale. -->
<script setup lang="ts">
import { ref, useId } from "vue";
import { DateField, type PlainDate } from "@vizejs/ui/date-field";
const birthday = ref<PlainDate | null>({ year: 1990, month: 4, day: 12 });
const labelId = useId();
const max: PlainDate = { year: 2026, month: 1, day: 1 };
function formatDate(date: PlainDate): string {
return `${date.year}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}`;
}
</script>
<template>
<div>
<span :id="labelId">Date of birth</span>
<DateField v-model="birthday" name="birthday" locale="en-GB" :max :aria-labelledby="labelId" />
<output>{{ birthday ? formatDate(birthday) : "No date entered" }}</output>
</div>
</template>
API
DateField
Source: src/families/date-time/date-field/date-field.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null | undefined |
undefined |
Consumer-owned group id; nullish values use a deterministic fallback. |
name |
string | undefined |
undefined |
Hidden input name that submits the ISO date (YYYY-MM-DD) with forms. |
modelValue |
PlainDate | null | undefined |
undefined |
Controlled date; undefined selects uncontrolled mode and null clears. |
defaultValue |
PlainDate | null | undefined |
null |
Initial uncontrolled date and native form-reset target. |
min |
PlainDate | null | undefined |
undefined |
Earliest valid date; later values mark the field invalid. |
max |
PlainDate | null | undefined |
undefined |
Latest valid date; earlier values mark the field invalid. |
isDateUnavailable |
DateMatcher | undefined |
undefined |
Predicate for dates that are entered but invalid. |
placeholderValue |
PlainDate | null | undefined |
undefined |
Date whose segments seed arrow-key stepping from empty; defaults to the host date at key time. |
timeZone |
string | undefined |
undefined |
IANA time zone for the key-time host date fallback. |
locale |
string | undefined |
undefined |
BCP 47 locale deciding segment order and separators; defaults to the nearest LocaleProvider. |
dir |
"ltr" | "rtl" | undefined |
undefined |
Text direction for arrow-key segment movement; defaults to the nearest LocaleProvider. |
disabled |
boolean | undefined |
false |
Remove every segment from focus and editing. |
readOnly |
boolean | undefined |
false |
Keep segments focusable while blocking edits. |
required |
boolean | undefined |
false |
Require a complete value; participates in native form validation. |
invalidMessage |
string | undefined |
"Invalid value" |
Custom validity message reported while the value is out of range or unavailable. |
placeholders |
FieldSegmentPlaceholders | undefined |
{ year: "yyyy", month: "mm", day: "dd" } |
Placeholder text for empty segments. |
emptyText |
string | undefined |
"Empty" |
Text announced for empty segments. |
ariaLabel |
string | undefined |
undefined |
Accessible name for the segment group. |
ariaLabelledby |
string | undefined |
undefined |
Ids that label the segment group. |
ariaDescribedby |
string | undefined |
undefined |
Ids that describe the group and every segment. |
ariaErrormessage |
string | undefined |
undefined |
Id of the validation message used while invalid. |
ariaInvalid |
boolean | undefined |
false |
Force the invalid state in addition to built-in min/max/availability validation. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: PlainDate | null] |
Fired when segments request a new controlled date or null while incomplete. |
change |
[value: PlainDate | null, previous: PlainDate | null, nativeEvent: Event | null] |
Fired after a distinct committed date with the previous date and triggering event. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
DateFieldSlotState |
Extra content after the segments, such as a picker trigger. Receives field state. |
segment |
FieldSegmentState |
Segment content. Receives the segment state; defaults to its text. |
Behavior
Normative state x input -> outcome table for date-field.vue
(@vizejs/ui/date-field), a segmented spinbutton date input. The segment
engine (field-segments.ts, field-segment-runtime.ts) is shared with
TimeField. Every row is proven by the named test.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| F1 | any | render | a role="group" holds role="spinbutton" segments in the locale's order with aria-hidden literals; segments expose value, bounds, aria-valuetext, localized Intl.DisplayNames labels, and contenteditable + inputmode for virtual keyboards; name adds a named ISO input (see F15) |
renders locale-ordered spinbutton segments with literals and a hidden ISO input |
| F2 | empty | digits | digits fill the focused segment and advance when no further digit fits; a complete set commits and emits update:modelValue then change |
typing digits fills segments, auto-advances, and commits a complete date |
| F3 | typing | leading zero / overflow | a lone 0 waits for a second digit; a digit that would overflow restarts the buffer |
a leading zero waits for a second digit and out-of-range digits restart the buffer |
| F4 | filled | Arrow / Page / Home / End | steps wrap within bounds (Page steps: year 10, month 3, day 7); day clamps to the month length when month or year changes | arrow, page, home, and end keys step segments with wrapping and day clamping |
| F5 | empty | Arrow | stepping an empty segment starts from placeholderValue (else the host date read at key time, never during render) |
stepping an empty segment starts from placeholderValue |
| F6 | filled | Backspace / Delete | Backspace drops the last digit, then moves to the previous segment when empty; Delete clears; any cleared segment commits null |
Backspace removes digits then moves back, Delete clears, and clearing commits null |
| F7 | focused | ArrowLeft / ArrowRight / other keys | horizontal arrows move between segments and flip in RTL; unhandled printable keys are prevented so the DOM stays controlled | horizontal arrows move between segments and honor RTL |
| F8 | virtual keyboard | beforeinput |
insertText and deleteContentBackward are handled and always prevented |
beforeinput supports virtual keyboards without mutating the DOM |
| F9 | min / max / predicate | commit | values outside the window or rejected by isDateUnavailable, or ariaInvalid, set data-state="invalid", aria-invalid, and aria-errormessage |
min, max, unavailable dates, and ariaInvalid mark the field invalid |
| F10 | disabled / read-only | render / keys | disabled segments stay focusable with aria-disabled, lose contenteditable, and the hidden input is disabled; read-only allows segment navigation but no edits |
disabled and read-only fields keep availability and form semantics |
| F11 | controlled / form | edit / prop change / form reset | controlled edits revert until accepted; external values replace every segment; native form reset restores defaultValue |
controlled values win, external values replace segments, and form reset restores defaults |
| F12 | imperative | expose | focus(segment?), setValue, clear, reset, and normalized state |
exposes focus, setValue, clear, reset, and normalized state |
| F13 | pure helpers | call | bounds, stepping, typing, and locale layouts are deterministic functions | segment helpers expose pure bounds, stepping, typing, and layouts |
| F15 | required / name |
form validation | a visually hidden, aria-hidden, tabindex="-1" text input carries name, the ISO value, and required; empty required fields fail checkValidity() with valueMissing and move focus to the first empty segment; out-of-range values report invalidMessage through setCustomValidity |
required fields participate in native constraint validation |
| F14 | SSR/hydration | isolated requests | byte-identical segments and deterministic segment ids hydrate silently | renders byte-identical locale-ordered date segments across SSR requests, hydrates date segments with generated ids and no mismatches |
Extension hooks
| Hook | Values |
|---|---|
| parts | root, segment, literal |
| root data | data-vize-ui="date-field", data-state (empty/partial/complete/invalid), data-value, data-dir, data-invalid, data-disabled, data-readonly, data-required |
| segment data | data-segment, data-placeholder, data-invalid |
| slots | segment (segment content), default (content after the segments, such as a picker trigger) |