Date Picker <mv-date-picker>

Date picker: an input-style trigger showing the formatted date (ranges included) and a calendar in a popover, with presets like “Today”, “Last 7 days”…, and an optional time row for date and time values; form-associated.

CategoryForms
TypeWeb Component (<mv-date-picker>)
Statusstable
Also installscalendar, time-picker
Keywordsdate-picker, datepicker, date, range, presets, popover, datetime, date-time, form-associated

When to use

  • A form needs a single date, like a birth date or delivery day, without taking permanent space
  • A date range such as a booking stay or a report period must be picked in a compact trigger
  • Analytics filters need quick presets like Today or Last 7 days next to a calendar
  • A pickup, meeting or deadline needs a day and a time together in one field

Avoid when

  • The calendar should stay visible on the page as part of the layout → use Calendar instead
  • A touch-first flow picks a time or date by spinning columns, iOS style → use Wheel Picker instead
  • The date is far in the past and known by heart, like a birth year; plain typed fields are faster

Install

node scripts/add.mjs date-picker --out ./src/marvelous

AI agent with the Marvelous UI MCP server: install_components({ slugs: ["date-picker"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Files copied (dependencies included): tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/i18n.js, core/motion.js, components/calendar/calendar.js, components/calendar/calendar.css, core/dismiss.js, core/focus.js, core/form.js, core/position.js, components/time-picker/time-picker.js, components/time-picker/time-picker.css, components/date-picker/date-picker.js, components/date-picker/date-picker.css.

Usage

Canonical markup, to start from and customize with attributes, data-* and CSS variables:

<form id="date-picker-demo-form" style="display:flex;flex-wrap:wrap;gap:1.25rem;justify-content:center;align-items:flex-start">
  <div style="display:grid;gap:.4rem">
    <label for="date-picker-demo-delivery" style="font-size:.875rem;font-weight:500">Delivery date</label>
    <mv-date-picker id="date-picker-demo-delivery" name="delivery" min="2026-09-23" disable-weekends clearable presets placeholder="Pick a business day"></mv-date-picker>
  </div>
  <div style="display:grid;gap:.4rem">
    <label for="date-picker-demo-period" style="font-size:.875rem;font-weight:500">Report period</label>
    <mv-date-picker id="date-picker-demo-period" name="period" mode="range" presets value="2026-09-01/2026-09-22" format="long" style="--mv-date-picker-width:19rem"></mv-date-picker>
  </div>
  <div style="display:grid;gap:.4rem">
    <label for="date-picker-demo-pickup" style="font-size:.875rem;font-weight:500">Airport pickup</label>
    <mv-date-picker id="date-picker-demo-pickup" name="pickup" time time-step="30" time-min="06:00" time-max="22:00" min="2026-09-23" value="2026-10-02T14:30" style="--mv-date-picker-width:15rem"></mv-date-picker>
  </div>
</form>

API

Attributes

NameTypeDefaultDescription
modesingle | range | multiplesingleSelection type (forwarded to the calendar).
valuestringInitial ISO value (“2026-09-22” or “2026-09-01/2026-09-22”).
namestringForm field name (ISO value).
placeholderstringPick a dateText shown when nothing is selected.
formatshort | medium | long | fullmediumIntl style of the displayed date (ranges via formatRange: “Sep 1-22, 2026”).
presetsbooleanShows the default presets column (depends on the mode).
clearablebooleanButton to clear the date.
requiredbooleanNative validation; a range must be complete.
disabledbooleanDisables the trigger.
invalidbooleanError style + aria-invalid.
placementbottom-start | top-start…bottom-startPopover placement (flipped when there is no room).
min / max / disabled-dates / disable-weekends / months / locale / week-start / today-Forwarded as-is to <mv-calendar>. Locale: The value auto follows the page language (nearest lang); without the attribute, en-US unless the app registered the page language with setStrings() (core/i18n.js).
timebooleanDate and time mode (single mode only): a time field and a Done button sit under the calendar and the value becomes “2026-10-02T14:30”, like datetime-local. Picking a day then moves to the time; Done, Enter in the time, Escape or a click outside closes.
time-step / time-min / time-max / hour-cycle / seconds-Time mode: forwarded to the inner <mv-time-picker> as step, min, max, hour-cycle and seconds (with seconds the value is “…T14:30:00”).
labelstringAccessible name without a <label for>.
data-sizesm | lgTrigger height.

Properties

NameTypeDescription
valuestringISO value (also accepts a Date). In time mode “YYYY-MM-DDTHH:MM”, or “” until both the day and the time are set.
valueAsDateDate | nullFirst date (with its time in time mode).
range{ start, end }Range bounds.
presetListArray<{ label, key?, value: string | (today: Date) => string }>Custom presets (shown even without the presets attribute). An optional key names the strings entry that gives the button text (the default presets use it).
calendarMvCalendarInner calendar (isDateDisabled, goTo…).
isOpenbooleanWhether the popover is open.
timePickerMvTimePicker | nullInner time field in time mode.
stringsPartial<Record<string, string | { one, other }>>Overrides for the default text (en-US): clearDate (clear button label), presets (preset group label), placeholderDate, placeholderRange and placeholderMultiple (empty trigger text and dialog label), rangeOpen (range without an end, {start}), datesSelected (multiple mode, {n}, { one, other }), missingDate and missingRange (required messages), presetToday, presetTomorrow, presetInAWeek, presetInAMonth, presetLast7Days, presetLast30Days, presetThisMonth, presetLastMonth and presetThisYear (default preset buttons; a custom preset with a key reads its text here), and for the time attribute: time, done, placeholderDateTime, missingDateTime, missingTime. The inner calendar has its own strings (key "calendar"). App-wide translations: setStrings() from core/i18n.js, under the key "date-picker". Can be set before the element is defined.

Methods

NameDescription
open() / close({ focus })Opens or closes the popover.
clear()Clears the value.
checkValidity() / reportValidity()Native validation.

Events

NameDescription
mv-changedetail: { value, date, start, end, complete, preset? }; in time mode also time (“14:30”), and date carries the time.
mv-open / mv-closePopover opened and closed.

CSS classes

NameDescription
mv-date-picker-trigger / -value / -clear / -popup / -presets / -presetGenerated parts.
mv-date-picker-main / -time / -time-label / -doneTime mode parts: calendar and time column, time row, its label and the Done button.

CSS variables

NameDefaultDescription
--mv-date-picker-width16rem (18rem for ranges)Trigger width.
--mv-date-picker-radiusvar(--mv-radius-md)Trigger radius.

Accessibility

<button> trigger with aria-haspopup=dialog and aria-expanded, named by the <label for> + the displayed value. The popover is a labeled non-modal role=dialog; on open, focus moves to the calendar's active day (full APG grid), Escape or picking a date/range closes it and returns focus to the trigger; tabbing out also closes it. ↓ on the trigger opens it. Presets are aria-pressed buttons. In time mode the time field follows the calendar in the tab order (its segments are spinbuttons), then the Done button.