A calendar date picker with single-date and date-range modes. Renders a text input that opens a floating calendar panel. Dates are represented as ISO 8601 strings (YYYY-MM-DD).
<x-date-picker></x-date-picker>
| Attribute | Type | Default | Description |
|---|---|---|---|
mode | enum | "single" | Selection mode: single range |
value | string | — | Selected date in YYYY-MM-DD format (single mode) |
start | string | — | Range start date in YYYY-MM-DD format (range mode) |
end | string | — | Range end date in YYYY-MM-DD format (range mode) |
min | string | — | Minimum selectable date (YYYY-MM-DD) |
max | string | — | Maximum selectable date (YYYY-MM-DD) |
format | enum | "iso" | Display format: iso (YYYY-MM-DD) or localized (via Intl) |
locale | string | — | BCP 47 locale tag for localized formatting (e.g. "en-US") |
separator | string | " - " | String placed between start and end in range display |
auto-swap | boolean | false | Automatically swap start/end when end is selected before start |
range-allow-same-day | boolean | false | Allow start and end to be the same date in range mode |
close-on-select | boolean | false | Close the calendar after a date is picked, a range is complete, or typed text is committed with Enter |
placeholder | string | — | Input placeholder text |
disabled | boolean | false | Disables the input and calendar |
readonly | boolean | false | Input is read-only; calendar can still open but not select |
required | boolean | false | Marks the field as required |
name | string | — | Form field name |
autocomplete | string | — | HTML autocomplete hint |
error | string | — | Inline validation message; shows the error part and marks the field invalid |
aria-label | string | — | Accessible label for the input |
aria-describedby | string | — | References a describing element |
| Property | Type | Reflects attribute |
|---|---|---|
mode | string | mode |
value | string | value |
start | string | start |
end | string | end |
disabled | boolean | disabled |
readOnly | boolean | readonly |
required | boolean | required |
open | boolean | open |
error | string | error |
name | string | name |
min | string | min |
max | string | max |
format | string | format |
locale | string | locale |
separator | string | separator |
placeholder | string | placeholder |
autocomplete | string | autocomplete |
autoSwap | boolean | auto-swap |
rangeAllowSameDay | boolean | range-allow-same-day |
closeOnSelect | boolean | close-on-select |
| Event | Cancelable | Detail | Description |
|---|---|---|---|
x-date-picker-input | no | { value, mode } | Fired on each user text input |
x-date-picker-change-request | yes | { value, date, start, end, mode, reason } | Fired before committing a date selection |
x-date-picker-change | no | { value, date, start, end, mode, reason } | Fired after the selection is committed |
reason is one of "click", "keyboard", "blur", or "programmatic".
Every key is always present and null where the current mode has nothing to say, so a handler null-checks rather than testing for a missing key. In single mode value holds the ISO date string. In range mode start and end hold them, and end is null while the range is open. On a range click they hold the range that the click asks for, and date holds the day clicked. date is null otherwise. After an accepted request, x-date-picker-change carries what the request asked.
A null field asks for no attribute, so a request asks for no attribute of the other mode. Give a picker only the attributes of its mode.
Deleting the text and committing (blur, Enter, or commit()) clears the committed value, exactly as clear() does. It fires a cancelable x-date-picker-change-request first, then x-date-picker-change, both with null in value, start and end. Cancelling the request keeps the committed value and restores the input text.
Committing a blank input when nothing is committed yet is a no-op: neither event fires.
The calendar shows the month of value, or of start in range mode. When that date changes, also when a page writes it, the calendar moves to its month. Otherwise it keeps the month the user browsed to. So in range mode, picking the end in a later month keeps that month. Opening the calendar shows the month of the date again.
| Slot | Description |
|---|---|
| (default) | No default slot content expected |
role="combobox" with aria-expanded (synced to open state) and aria-haspopup="dialog".role="dialog" with aria-modal="true".aria-live="polite" for screen reader announcements.role="gridcell" with aria-selected and aria-disabled.aria-live="polite", aria-atomic="true") is provided.error attribute is set, the input gets aria-invalid="true" and its aria-describedby points at the error part (appended to any author-supplied aria-describedby). The error part is an assertive live region (role="alert") so screen readers announce the validation message.x-date-picker is a form-associated custom element (via ElementInternals). In a <form>:
name: the ISO date in single mode, or start/end once a range is complete.required picker with no committed date reports valueMissing and blocks submission; setting the error attribute reports a customError. This is what x-form and native submission gate on.form.reset() clears the committed value(s) (mirroring how x-form-field resets) and drops the error attribute, so a stale validation message does not survive the reset. <fieldset disabled> disables it via formDisabledCallback.Like a native form control, x-date-picker exposes read-only validity, validationMessage, willValidate, form and labels, plus checkValidity() and reportValidity(). All seven delegate to the element's ElementInternals, so el.checkValidity() answers the same question a submit attempt would.
| Key | Action |
|---|---|
ArrowLeft | Move one day back |
ArrowRight | Move one day forward |
ArrowUp | Move one week back |
ArrowDown | Move one week forward |
PageUp | Move one month back |
PageDown | Move one month forward |
Home | Go to first day of week |
End | Go to last day of week |
Enter/Space | Select focused date |
Escape | Close calendar without selecting |
| Click outside | Close calendar without selecting |
<x-date-picker placeholder="Select a date"></x-date-picker>
<x-date-picker mode="range" placeholder="Select date range"></x-date-picker>
<x-date-picker value="2026-03-18"></x-date-picker>
<x-date-picker min="2026-01-01" max="2026-12-31"></x-date-picker>
<x-date-picker format="localized" locale="en-US" placeholder="Pick a date"></x-date-picker>
<x-date-picker disabled placeholder="Not available"></x-date-picker>
The error attribute renders an inline message below the field and marks it
invalid. Inside x-form, this is driven for you by form.setFieldError(name, message).
<x-date-picker name="dob" error="Please enter a valid date"></x-date-picker>
Clear it by removing the attribute (or el.error = ''). Style the message via the
error CSS part or the --x-date-picker-error-color custom property.
document.querySelector('x-date-picker').addEventListener('x-date-picker-change', e => {
console.log('selected date:', e.detail.value);
});
[:x-date-picker {:placeholder "Select a date"
:on-x-date-picker-change
(fn [e] (swap! state assoc :date (.. e -detail -value)))}]
[:x-date-picker {:mode "range"
:placeholder "Select date range"
:on-x-date-picker-change
(fn [e]
(swap! state assoc
:start (.. e -detail -start)
:end (.. e -detail -end)))}]
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |