Skip to content

Date Picker

Allows users to enter a date either by typing it directly or by selecting it from a calendar popover. The component combines text input flexibility with a visual calendar to simplify accurate date selection.

Usage

Use the Date Picker when users need to input a specific date while providing both manual entry and visual selection options. For scenarios requiring only simple date selection, a Calendar Inline alone may suffice.

Keyboard Handling

The user can use the following keyboard shortcuts in order to navigate trough the date picker:

  • Up / Down - Moves focus to the day above/below the current day.
  • Right - Moves focus to the next day.
  • Left - Moves focus to the previous day.
  • Enter / Space - Shows and moves focus the calendar. If already shown, selects the focused day.
  • Home - Selects the first day of the month.
  • End - Selects the last day of the month.
  • PageUp - Selects the same or closest day of the previous month.
  • PageDown - Selects the same or closest day of the next month.
  • Esc - Closes the date picker calendar.

API Reference

Component attribute(s)

x-h-date-picker
x-h-date-picker-trigger
x-h-date-picker-popup

Attributes

x-h-date-picker

AttributeValuesRequiredDescription
data-sizesm
default
falseChanges the size of the date picker.

x-h-date-picker-popup

AttributeValuesRequiredDescription
data-alignbottom-start
bottom
bottom-end
right-start
right
right-end
left-start
left
left-end
top-start
top
top-end
falseAligns the calendar popup relative to the date picker trigger.
data-aria-prev-yearstringfalseSets the aria-label attribute value for the previous year button.
data-aria-prev-monthstringfalseSets the aria-label attribute value for the previous month button.
data-aria-next-monthstringfalseSets the aria-label attribute value for the next month button.
data-aria-next-yearstringfalseSets the aria-label attribute value for the next year button.

Modifiers

ModifierDescription
tableUse when the input is inside a table

Configuration

You can pass a configuration object to the popup as an expression or as a value.

KeyDescription
localeThe locale of the calendar as a BCP 47 language tag. If not provided, it is taken from the page's <html lang> attribute, then the browser locale.
firstDayThe start day of the week. 0 is Sunday.
minThe earliest date selectable. Must be provided in the standard ISO 8601 format - YYYY-MM-DD.
maxThe latest date selectable. Must be provided in the standard ISO 8601 format - YYYY-MM-DD.
optionsIntl.DateTimeFormat options.
delimiterCustom separator character between day, month, and year in the display format (e.g. "-"). Does not affect the model value.
orderCustom display order of the date parts as a three-character string of Y (year), M (month), D (day) (e.g. "MDY" for month-day-year). Defaults to the locale's natural order. Does not affect the model value.
rangeWhen true, the picker selects a start-and-end date range instead of a single date. See Range selection.
rangeSeparatorText placed between the two dates in the display input when range is enabled. Defaults to " - ".

Model

The date picker reads and writes dates as YYYY-MM-DD strings (e.g. "2025-06-09"), matching the value format of a native <input type="date">. The display format shown in the text input is separate and can be customised via the options key in the calendar config.

Events

EventDescription
changeFired on the inner input when the user picks a date from the calendar or types a valid date. The event bubbles, so a listener can be placed on the x-h-date-picker element. Read the new value from the bound model, as the input's own value holds the formatted display text instead.

There is no need to use $watch to react to user selection - listen for change instead. See Listening for changes.

Display format

By default the input displays the date using the user's locale. To customise it, pass Intl.DateTimeFormat options via the options key on x-h-date-picker-popup. The model value always remains YYYY-MM-DD regardless of the display format.

html
<div x-h-date-picker ...>
  <input type="text" />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup="{ options: { day: '2-digit', month: '2-digit', year: 'numeric' } }" x-model="date"></div>
</div>

Manual input typed by the user is parsed using the configured display format. For formats where the month appears as a word rather than a number, parsing falls back to the browser's native Date constructor.

Range selection

Set range: true on the popup config to let the user pick a start-and-end date range. The first click selects the start, the second completes the range (picks are ordered automatically, so clicking an earlier day second still produces a valid range). With the keyboard, press Enter once to set the start and again to set the end.

In range mode the model value is an object with start and end keys (each a YYYY-MM-DD string), and the input displays both dates joined by the rangeSeparator (default " - "):

js
{ start: '2025-06-09', end: '2025-06-16' }
html
<div x-h-date-picker x-data="{ range: { start: '', end: '' } }">
  <input type="text" id="date-input-range" />
  <button x-h-date-picker-trigger aria-label="Choose date range"></button>
  <div x-h-date-picker-popup="{ range: true }" x-model="range"></div>
</div>

Validation timing

By default this control shows native-constraint errors (for example required) only after the user interacts with it or attempts to submit, not on page load. To validate on load instead, set data-validate="immediate" on a wrapping x-h-fieldset, x-h-field, or any ancestor element. Setting aria-invalid="true" yourself always shows the error immediately. See Fieldset for details.

Examples

html
<div
  x-h-date-picker
  x-data="{
  date: '',
  init() {
    const d = new Date();
    this.date = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
  }
}"
>
  <input type="text" id="date-input-1" />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup x-model="date"></div>
</div>

Listening for changes

html
<div x-h-date-picker x-data="{ date: '' }" @change="console.log('Selected date:', date)">
  <input type="text" id="date-input-change" />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup x-model="date"></div>
</div>

With locale

html
<div
  x-h-date-picker
  x-data="{
  date: '',
  init() {
    const d = new Date();
    this.date = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
  }
}"
>
  <input type="text" id="date-input-locale" />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup="{ locale: 'bg-BG', firstDay: 1 }" x-model="date"></div>
</div>

With custom display format

html
<div
  x-h-date-picker
  x-data="{
  date: '',
  init() {
    const d = new Date();
    this.date = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
  }
}"
>
  <input type="text" id="date-input-2" />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup="{ options: { day: '2-digit', month: 'long', year: 'numeric' }, order: 'DMY' }" x-model="date"></div>
</div>

Invalid

Reacts to the native invalid state or to the aria-invalid attribute.

html
<div x-h-date-picker x-data="{ date: '2026-07-09' }">
  <input type="text" id="date-input-invalid" aria-invalid="true" />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup x-model="date"></div>
</div>

Disabled

Set the native disabled attribute on the inner input to disable the whole picker.

html
<div x-h-date-picker x-data="{ date: '2026-07-09' }">
  <input type="text" id="date-input-disabled" disabled />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup x-model="date"></div>
</div>

Read-only

Set the native readonly attribute on the inner input. The value is shown with a muted background, and neither typing nor the calendar popover can change it.

html
<div x-h-date-picker x-data="{ date: '2026-07-09' }">
  <input type="text" id="date-input-readonly" readonly />
  <button x-h-date-picker-trigger aria-label="Choose date"></button>
  <div x-h-date-picker-popup x-model="date"></div>
</div>