DatePicker
DatePicker combines a date-field trigger with Calendar. Choose it when one local date belongs in a compact form; choose Calendar when the date grid should remain visible in the layout.
Examples
A selected start date
Use an ISO YYYY-MM-DD string for a date-only initial value. The field formats the selected local
date with its locale, and Calendar uses that same locale.
A delivery window
min and max pass through to the composed Calendar to prevent selection outside the permitted
local-date window. Add hint when the constraint needs a short explanation.
When to use
Use DatePicker for a single local date that should occupy one compact form field.
Reach for something else when:
- Both a start and end date are required — use DateRangePicker, which selects the range in one Calendar and displays it on one trigger.
- The date grid needs to remain visible — use Calendar directly.
- A date is part of a time-slot choice — pair DatePicker with TimePicker rather than asking a date control to represent a time.
Accessibility
- The trigger is a native button with its visible
labelassociated throughhtmlFor. On desktop, it is passed to Popover, which supplies its dialog trigger semantics, Escape handling and focus return. - The composed Calendar supplies roving day focus, keyboard date navigation and locale-formatted date labels. Its selected date is passed back to the field and closes the picker.
errorreplaceshintand applies error styling to the trigger.disableddisables the trigger.- At viewports of 640px or less, the desktop Popover is replaced with a BottomSheet containing the
Calendar. Its title comes from
labelorlabels.sheetTitle;labels.closenames its close button. This path is documented only because the stage cannot resize its viewport. labels.calendarforwards translated Calendar controls, whilelocaleformats both the trigger and Calendar dates.
Alpine mobile return focus
Configure the existing mobile BottomSheet with a return resolver. It resolves on close from that
sheet's enclosing DatePicker root, so each currently rendered trigger remains local when multiple
pickers share a page. This explicit destination uses focus({ preventScroll: true }) after a native
pointer selection. Without it, BottomSheet only falls back to an eligible opener captured before a
keyboard opening; an unprepared WebKit pointer opening must not rely on that fallback.
<div class="lyra-datepicker-root" x-data="lyraDatePicker({ locale: 'en-US' })">
<div class="lyra-datepicker">
<button class="lyra-input lyra-datepicker__btn" type="button" @click="open = true">
Select date
</button>
</div>
<div x-data="{ get pickerOpen() { return open }, set pickerOpen(v) { open = v } }">
<div
x-data="lyraBottomSheet({
returnFocusTo: () => $el.closest('.lyra-datepicker-root')?.querySelector('.lyra-datepicker__btn') ?? null,
})"
x-modelable="open"
x-model="pickerOpen"
>
<!-- Serve the existing BottomSheet overlay, panel, and Calendar markup here. -->
</div>
</div>
</div>API and code
| Name | Type | Required | Description |
|---|---|---|---|
label | string | — | Label rendered above the picker trigger. |
hint | string | — | Helper text rendered below the picker. Replaced by `error` when set. |
error | string | — | Error message that enables error styling and replaces `hint`. |
value | Date | string | null | — | Controlled local date or ISO `YYYY-MM-DD` date-only string, or `null` with no date selected. |
defaultValue | Date | string | — | Initial local date or ISO `YYYY-MM-DD` date-only string in uncontrolled mode. |
onChange | (date: Date) => void | — | Called after the user selects a local date. |
placeholder | string | — | Trigger text when no date is selected. Defaults to `labels.placeholder`. |
min | Date | string | — | Inclusive lower selection limit as a local date or ISO `YYYY-MM-DD` string. |
max | Date | string | — | Inclusive upper selection limit as a local date or ISO `YYYY-MM-DD` string. |
locale | string | — | BCP 47 locale used to format the selected date and compose Calendar. Default: `"en-US"`. |
labels | DatePickerLabels | — | Translatable visible and accessible labels, merged over the English defaults. |
disabled | boolean | — | Disables the picker trigger. |
x-data="lyraDatePicker({ … })"
| Option | Type | Required | Description |
|---|---|---|---|
defaultValue | string | — | Initial ISO `YYYY-MM-DD` date. |
locale | string | — | BCP 47 locale used for the trigger's selected-date text. Default: `"en-US"`. |
placeholder | string | — | Trigger text when no date is selected. Default: `"Select date"`. |
Without React, compose the trigger, Popover and Calendar classes and own the local-date, dialog and keyboard behavior:
<div class="lyra-field">
<label class="lyra-label" for="start-date">Start date</label>
<span class="lyra-datepicker lyra-popover-anchor">
<button
class="lyra-input lyra-datepicker__btn"
id="start-date"
type="button"
aria-haspopup="dialog"
aria-expanded="true"
>
<span>08/04/2026</span>
</button>
<div
class="lyra-popover lyra-popover--bottom lyra-popover--align-start"
role="dialog"
aria-label="Date picker"
>
<div class="lyra-cal">
<div class="lyra-cal__head">
<button class="lyra-cal__nav" type="button">‹</button
><button class="lyra-cal__label" type="button">August 2026</button
><button class="lyra-cal__nav" type="button">›</button>
</div>
<button class="lyra-cal__day lyra-cal__day--selected" type="button" aria-pressed="true">
4
</button>
</div>
</div>
</span>
</div><lyra:date-picker> Generated from lyra-ds/blade v0.10.0.
The behavior comes from lyraDatePicker() — install @lyra-ds/alpine and see the HTML + Alpine tab.
| Prop | Default | Required | Example values |
|---|---|---|---|
label | null | — | 0 Date |
hint | null | — | — |
error | null | — | — |
defaultValue | null | — | — |
placeholder | null | — | — |
min | null | — | — |
max | null | — | — |
locale | 'en-US' | — | — |
labels | [] | — | — |
disabled | false | — | — |
name | null | — | — |
<lyra:date-picker
name="due_date"
label="Due date"
hint="Pick a date within the current quarter."
default-value="2026-03-17"
min="2026-01-01"
max="2026-12-31"
locale="en-US"
/>