DatePicker
Three input fields that open a calendar: DatePicker picks a date, DateTimePicker picks a date and a time, and DateRangePicker picks a start and an end date. To pick only a time, use TimePicker.
<DatePicker
label="Due date"
placeholder="Pick a date"
v-model="value"
/>Examples
Booking forms
DatePicker with min and max for flights and birthdays, isDateUnavailable to block weekends and full days, #actions for shortcuts such as "Tomorrow", and #trigger for an "Add due date" button on a task.
<script setup lang="ts">
import { ref } from 'vue'
import { DatePicker, Button } from 'frappe-ui'
import { dayjs } from '../../../utils/dayjs'
import type { Dayjs } from 'dayjs/esm'
// 1. Flight booking — can't fly into the past
const departure = ref('')
const today = dayjs().format('YYYY-MM-DD')
const oneYearOut = dayjs().add(1, 'year').format('YYYY-MM-DD')
// 2. Date of birth — can't be born in the future
const dob = ref('1995-08-14')
const longAgo = dayjs().subtract(120, 'year').format('YYYY-MM-DD')
// 3. Appointment — weekdays only, next 30 days, with quick actions and
// a few slots already taken.
const appointment = ref('')
const inThirtyDays = dayjs().add(30, 'day').format('YYYY-MM-DD')
const fullyBooked = new Set([
dayjs().add(2, 'day').format('YYYY-MM-DD'),
dayjs().add(5, 'day').format('YYYY-MM-DD'),
dayjs().add(11, 'day').format('YYYY-MM-DD'),
])
function isUnavailable(date: Dayjs) {
const day = date.day()
if (day === 0 || day === 6) return true
return fullyBooked.has(date.format('YYYY-MM-DD'))
}
function nextMonday(from: Dayjs = dayjs()) {
const diff = (8 - from.day()) % 7 || 7
return from.add(diff, 'day')
}
// 4. Fun — pick a Stardate for a time-travel destination
const stardate = ref('')
const stardateFloor = dayjs('1800-01-01').format('YYYY-MM-DD')
const stardateCeil = dayjs('2300-12-31').format('YYYY-MM-DD')
// 5. Custom trigger — "Add due date" button on a task card
const dueDate = ref('')
const rowCls =
'w-full rounded-4 px-2 py-1.5 text-left text-base hover:bg-surface-gray-2'
</script>
<template>
<div class="grid w-full max-w-sm gap-6 py-10">
<!-- 1. Flight booking -->
<DatePicker
v-model="departure"
label="Departure"
placeholder="When are you flying?"
format="ddd, MMM D"
:min="today"
:max="oneYearOut"
>
<template #prefix>
<span class="lucide-plane size-4 text-ink-gray-5" />
</template>
</DatePicker>
<!-- 2. Date of birth -->
<DatePicker
v-model="dob"
label="Date of birth"
placeholder="MM/DD/YYYY"
format="MMM D, YYYY"
:min="longAgo"
:max="today"
:clearable="false"
>
<template #prefix>
<span class="lucide-cake size-4 text-ink-gray-5" />
</template>
</DatePicker>
<!-- 3. Appointment booking -->
<DatePicker
v-model="appointment"
label="Book appointment"
description="Weekdays only, next 30 days. Greyed-out slots are full."
placeholder="Pick a slot"
:min="today"
:max="inThirtyDays"
:is-date-unavailable="isUnavailable"
>
<template #prefix>
<span class="lucide-stethoscope size-4 text-ink-gray-5" />
</template>
<template #actions="{ setDate, close }">
<button
type="button"
:class="rowCls"
@click="
() => {
setDate(dayjs().add(1, 'day'))
close()
}
"
>
Tomorrow
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setDate(nextMonday())
close()
}
"
>
Next Monday
</button>
</template>
</DatePicker>
<!-- 4. Fun: Time-travel destination -->
<DatePicker
v-model="stardate"
label="Time-travel destination"
placeholder="When to?"
format="[Stardate] YYYY.MM.DD"
variant="outline"
:min="stardateFloor"
:max="stardateCeil"
>
<template #prefix>
<span class="lucide-rocket size-4 text-ink-gray-5" />
</template>
</DatePicker>
<!-- 5. Custom trigger — feels like a task-card affordance -->
<div class="flex flex-col gap-1.5">
<span class="text-sm text-ink-gray-7">Task card affordance</span>
<DatePicker v-model="dueDate" format="MMM D">
<template #trigger="{ setOpen, displayLabel, open }">
<Button
:variant="dueDate ? 'subtle' : 'ghost'"
@click="setOpen(!open)"
>
<template #prefix>
<span class="lucide-calendar-plus size-4" />
</template>
{{ dueDate ? `Due ${displayLabel}` : 'Add due date' }}
</Button>
</template>
</DatePicker>
</div>
</div>
</template>Scheduling a meeting
DateTimePicker for a meeting that cannot be in the past, a maintenance window with a min and a max, and #actions shortcuts that set a date and time.
<script setup lang="ts">
import { ref } from 'vue'
import { DateTimePicker } from 'frappe-ui'
import { dayjs } from '../../../utils/dayjs'
// 1. Meeting — can't schedule in the past, default to the next 30-min slot
const now = dayjs()
const nextSlot = now
.minute(now.minute() < 30 ? 30 : 60)
.second(0)
.format('YYYY-MM-DD HH:mm:ss')
const meeting = ref(nextSlot)
const minMeetingTime = now.format('YYYY-MM-DD HH:mm:ss')
// 2. Server maintenance window — must be at least 24h out, max 60 days
const maintenance = ref('')
const tomorrow = dayjs()
.add(1, 'day')
.startOf('day')
.format('YYYY-MM-DD HH:mm:ss')
const sixtyDaysOut = dayjs()
.add(60, 'day')
.endOf('day')
.format('YYYY-MM-DD HH:mm:ss')
// 3. Fun — schedule the great launch (with a "Pick a dramatic moment" preset)
const launch = ref('')
function nextFridayMidnight() {
const today = dayjs()
const diff = (5 - today.day() + 7) % 7 || 7
return today.add(diff, 'day').hour(0).minute(0).second(0)
}
const rowCls =
'w-full rounded-4 px-2 py-1.5 text-left text-base hover:bg-surface-gray-2'
</script>
<template>
<div class="grid w-full max-w-sm gap-6 py-10">
<!-- 1. Meeting scheduler -->
<DateTimePicker
v-model="meeting"
label="Meeting time"
placeholder="When should we meet?"
:min="minMeetingTime"
format="ddd, MMM D · h:mm A"
>
<template #prefix>
<span class="lucide-calendar-clock size-4 text-ink-gray-5" />
</template>
</DateTimePicker>
<!-- 2. Maintenance window -->
<DateTimePicker
v-model="maintenance"
label="Maintenance window"
description="Must be scheduled at least 24 hours in advance."
placeholder="Pick a low-traffic time"
:min="tomorrow"
:max="sixtyDaysOut"
>
<template #prefix>
<span class="lucide-server size-4 text-ink-gray-5" />
</template>
</DateTimePicker>
<!-- 3. Fun: dramatic launch moment -->
<DateTimePicker
v-model="launch"
label="Launch moment"
placeholder="When do we light the candle?"
format="MMMM D, YYYY [at] h:mm A"
variant="outline"
>
<template #prefix>
<span class="lucide-rocket size-4 text-ink-gray-5" />
</template>
<template #actions="{ setDate, close }">
<button
type="button"
:class="rowCls"
@click="
() => {
setDate(dayjs().add(1, 'day').hour(12).minute(0))
close()
}
"
>
High noon
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setDate(nextFridayMidnight())
close()
}
"
>
Midnight Friday
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setDate(dayjs().add(1, 'day').hour(6).minute(15))
close()
}
"
>
Sunrise
</button>
</template>
</DateTimePicker>
</div>
</template>Time off, stays and reports
DateRangePicker with shortcuts in #actions, dual-pane for hotel stays, isDateUnavailable for a sprint on weekdays only, and a #trigger that splits the range into "Depart" and "Return" fields over one calendar.
<script setup lang="ts">
import { computed, ref } from 'vue'
import { DateRangePicker, type DateRangeValue } from 'frappe-ui'
import { dayjs } from '../../../utils/dayjs'
import type { Dayjs } from 'dayjs/esm'
const today = dayjs().format('YYYY-MM-DD')
const oneYearOut = dayjs().add(1, 'year').format('YYYY-MM-DD')
// 1. PTO request — quick presets feel native to HR tools
const timeOff = ref<DateRangeValue>([])
// 2. Hotel booking — dual pane, check-in must be today or later
const stay = ref<DateRangeValue>([])
// 3. Analytics filter — preset-driven, displays compact format
const analyticsRange = ref<DateRangeValue>([
dayjs().subtract(29, 'day').format('YYYY-MM-DD'),
dayjs().format('YYYY-MM-DD'),
])
function longWeekend(): [Dayjs, Dayjs] {
const fri = upcomingDay(5)
return [fri, fri.add(2, 'day')]
}
function workWeek(): [Dayjs, Dayjs] {
const mon = upcomingDay(1)
return [mon, mon.add(4, 'day')]
}
function upcomingDay(weekday: number) {
const t = dayjs()
const diff = (weekday - t.day() + 7) % 7 || 7
return t.add(diff, 'day')
}
function lastNDays(days: number): [Dayjs, Dayjs] {
return [dayjs().subtract(days - 1, 'day'), dayjs()]
}
// Canonical sidebar row styling.
const rowCls =
'w-full rounded-4 px-2 py-1.5 text-left text-base hover:bg-surface-gray-2'
// 4. Project sprint — weekdays only, span limited to ~2 weeks via `max`
const sprint = ref<DateRangeValue>([])
const sprintEnd = dayjs().add(60, 'day').format('YYYY-MM-DD')
function isWeekend(date: Dayjs) {
const d = date.day()
return d === 0 || d === 6
}
// 5. Flight booking — custom #trigger renders two side-by-side inputs
// that share one popover, so the calendar still visualizes the range.
const flight = ref<DateRangeValue>([])
const depart = computed(() =>
flight.value[0] ? dayjs(flight.value[0]).format('ddd, MMM D') : '',
)
const ret = computed(() =>
flight.value[1] ? dayjs(flight.value[1]).format('ddd, MMM D') : '',
)
</script>
<template>
<div class="grid w-full max-w-sm gap-6 py-10">
<!-- 1. PTO request -->
<DateRangePicker
v-model="timeOff"
label="Time off"
placeholder="Pick your dates"
:min="today"
>
<template #prefix>
<span
class="lucide-palmtree size-4 text-ink-gray-5"
aria-hidden="true"
/>
</template>
<template #actions="{ setRange, close }">
<button
type="button"
:class="rowCls"
@click="
() => {
setRange(longWeekend())
close()
}
"
>
Long weekend
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setRange(workWeek())
close()
}
"
>
Next week
</button>
</template>
</DateRangePicker>
<!-- 2. Hotel booking — dual pane shines for >1 month stays -->
<DateRangePicker
v-model="stay"
label="Check-in / Check-out"
placeholder="Select your stay"
dual-pane
:min="today"
:max="oneYearOut"
>
<template #prefix>
<span class="lucide-hotel size-4 text-ink-gray-5" aria-hidden="true" />
</template>
</DateRangePicker>
<!-- 3. Analytics date range filter -->
<DateRangePicker
v-model="analyticsRange"
label="Report range"
description="Defaults to the last 30 days."
format="MMM D"
:max="today"
>
<template #prefix>
<span
class="lucide-chart-line size-4 text-ink-gray-5"
aria-hidden="true"
/>
</template>
<template #actions="{ fromDate, toDate, setRange, clear, close }">
<button
type="button"
:class="rowCls"
@click="
() => {
setRange([dayjs(), dayjs()])
close()
}
"
>
Today
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setRange(lastNDays(7))
close()
}
"
>
Last 7 days
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setRange(lastNDays(28))
close()
}
"
>
Last 4 weeks
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setRange(lastNDays(90))
close()
}
"
>
Last 3 months
</button>
<button
type="button"
:class="rowCls"
@click="
() => {
setRange(lastNDays(365))
close()
}
"
>
Last 12 months
</button>
<hr class="my-1 border-outline-gray-2" />
<button
v-if="fromDate || toDate"
type="button"
:class="rowCls"
@click="
() => {
clear()
close()
}
"
>
Clear
</button>
</template>
</DateRangePicker>
<!-- 4. Sprint window — weekdays only -->
<DateRangePicker
v-model="sprint"
label="Sprint window"
description="Pick start and end. Weekends are skipped."
:min="today"
:max="sprintEnd"
:is-date-unavailable="isWeekend"
>
<template #prefix>
<span
class="lucide-timer-reset size-4 text-ink-gray-5"
aria-hidden="true"
/>
</template>
</DateRangePicker>
<!-- 5. Flight booking — split trigger over one shared popover -->
<DateRangePicker v-model="flight" dual-pane :min="today">
<template #trigger="{ setOpen, open }">
<div
class="grid grid-cols-2 divide-x divide-outline-gray-2 rounded-4 border bg-surface-base text-sm transition-colors"
:class="
open
? 'border-outline-gray-4 ring-2 ring-outline-gray-2'
: 'border-outline-gray-2 hover:border-outline-gray-3'
"
>
<button
type="button"
class="flex items-center gap-2 rounded-l-4 px-3 py-2 text-left hover:bg-surface-gray-1"
@click="setOpen(!open)"
>
<span
class="lucide-plane-takeoff size-4 text-ink-gray-5"
aria-hidden="true"
/>
<div class="flex flex-col leading-tight">
<span class="text-xs text-ink-gray-5">Depart</span>
<span class="text-ink-gray-9">
{{ depart || 'Add date' }}
</span>
</div>
</button>
<button
type="button"
class="flex items-center gap-2 rounded-r-4 px-3 py-2 text-left hover:bg-surface-gray-1"
@click="setOpen(!open)"
>
<span
class="lucide-plane-landing size-4 text-ink-gray-5"
aria-hidden="true"
/>
<div class="flex flex-col leading-tight">
<span class="text-xs text-ink-gray-5">Return</span>
<span class="text-ink-gray-9">
{{ ret || 'Add date' }}
</span>
</div>
</button>
</div>
</template>
</DateRangePicker>
</div>
</template>Behavior
Values
| Picker | v-model |
|---|---|
DatePicker | 'YYYY-MM-DD' |
DateTimePicker | 'YYYY-MM-DD HH:mm:ss' |
DateRangePicker | [from, to] as 'YYYY-MM-DD', or [] |
format changes only the text shown in the input, not the value. Type a range with DateRangeValue, exported from frappe-ui.
Limits
min and max take 'YYYY-MM-DD'. DateTimePicker also takes 'YYYY-MM-DD HH:mm:ss'. isDateUnavailable receives each date as a Dayjs object and returns true to block it. It works together with min and max.
Typing a date
People can type a date into the input by default. Set :typeable="false" to allow picking from the calendar only. On DateTimePicker, typeable covers both the date and the time inputs.
Clearing
clearable is true by default, and an empty input clears the value. On DatePicker and DateTimePicker, :clearable="false" fills an empty input with today's date (and the current time) instead. DateRangePicker does not read clearable: emptying its input always clears the range.
Closing
The popover closes after a date is picked. Set keep-open to keep it open. DateTimePicker stays open after a date click, because the time is still missing, and moves focus to the time input.
Label, description and error
label renders above the field and description below it. error renders below the field and hides description. It takes a string, an array of strings (one line each), or an Error, the same values as ErrorMessage. An empty string or an empty array means no error. required adds a red asterisk to the label and sets required on the <input>.
The pickers have no #label or #description slot. A #trigger slot replaces the input, and the label, description and error go with it.
Slots
#trigger, #prefix and #suffix receive { open, disabled, setOpen, close, displayLabel, inputValue }. close() is the same as setOpen(false).
#actions renders a column of shortcuts beside the calendar. It receives open, disabled, setOpen, close and clear, plus:
| Picker | Extra #actions props |
|---|---|
DatePicker | selected, setDate |
DateTimePicker | selected, time, setDate |
DateRangePicker | fromDate, toDate, setDate, setRange |
Accessibility
The input has role="combobox", aria-haspopup="dialog" and aria-expanded, so a screen reader announces that the field opens a panel and whether the panel is open.
API Reference
DatePicker
Show types
import type { Dayjs } from 'dayjs/esm'
import type { InputSize, InputVariant } from '../../composables/inputTypes'
import type { InputLabelingProps } from '../../composables/useInputLabeling'
// Re-exported, not redeclared: `index.ts` does `export * from './types'`, so a
// local copy publishes a second structurally identical `PopoverSide` at the
// package root that can drift from `Popover`'s without a compile error.
import type { PopoverAlign, PopoverSide } from '../Popover/types'
export type { PopoverAlign, PopoverSide }
// Shared props for both single date and range pickers
export interface CommonDatePickerProps extends InputLabelingProps {
// Positioning — aligned with Combobox/Dropdown vocabulary
/** Preferred popover side relative to the trigger. */
side?: PopoverSide
/** Alignment of the popover along the trigger edge. */
align?: PopoverAlign
/** Gap between the trigger and popover content in pixels. */
offset?: number
// Display
/** Display format used for the input text. */
format?: string
/** Size of the trigger input. */
size?: InputSize
/** Visual style variant passed through to the input. */
variant?: InputVariant
/** Placeholder text shown when no value is selected. */
placeholder?: string
// Interaction
/** Controls popover open state (for controlled usage). */
open?: boolean
/** Opens the popover when the input receives focus. Default: false. */
openOnFocus?: boolean
/** Opens the popover when the input is clicked. Default: true. */
openOnClick?: boolean
/**
* Whether the trigger input accepts typed input. When `false` the user can
* still open the popover and pick a date, but cannot type a date manually.
* Default: `true`.
*/
typeable?: boolean
/** Disables the trigger input and calendar interactions. */
disabled?: boolean
/** Shows clear and quick-action controls when enabled. */
clearable?: boolean
/** Keeps the popover open after a date is selected. Default: false. */
keepOpen?: boolean
// Constraints
/**
* Earliest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`,
* `YYYY-MM-DD HH:mm:ss` for second-level granularity).
*/
min?: string
/**
* Latest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`,
* `YYYY-MM-DD HH:mm:ss` for second-level granularity).
*/
max?: string
/** Return true to prevent a date from being selected. Combined with `min`/`max`. */
isDateUnavailable?: (date: Dayjs) => boolean
}
export interface DatePickerProps extends CommonDatePickerProps {
/** Controlled value for the picker. */
modelValue?: string
}
/** A range value: a `[from, to]` tuple in `YYYY-MM-DD` format, or `[]` when cleared. */
export type DateRangeValue = [string, string] | []
export interface DateRangePickerProps extends CommonDatePickerProps {
/**
* Controlled range value as `[from, to]` in `YYYY-MM-DD` format, or `[]` for
* no selection. `DateRangeValue` types both sides of `v-model` (INP-Q12): the
* prop was `string[]`, which let a one-element array in and made a round-trip
* through the model fail to type-check.
*/
modelValue?: DateRangeValue
/** Render two calendar panels side by side (current month + next month). */
dualPane?: boolean
}
export type DatePickerEmits = {
/** Fired when the picker value changes. */
(event: 'update:modelValue', value: string): void
/** Fired when the popover open state changes. */
(event: 'update:open', value: boolean): void
/** Fired after the picker commits a normalized value. */
(event: 'change', value: string): void
}
export type DateRangePickerEmits = {
/** Fired when the range value changes. Emits `[from, to]` or `[]` when cleared. */
(event: 'update:modelValue', value: DateRangeValue): void
/** Fired when the popover open state changes. */
(event: 'update:open', value: boolean): void
/** Fired after the picker commits a normalized range value. */
(event: 'change', value: DateRangeValue): void
}
export interface DateTimePickerProps extends CommonDatePickerProps {
/** Controlled value for the picker. */
modelValue?: string
}
export type DateTimePickerEmits = DatePickerEmits
// ── Slot prop shapes ─────────────────────────────────────────────────────────
/** Props bound to the trigger / prefix / suffix slots on all three pickers. */
export interface DatePickerTriggerSlotProps {
/** Whether the popover is currently open. */
open: boolean
/** Whether the picker is disabled. */
disabled: boolean
/** Sets the popover open state. */
setOpen: (value: boolean) => void
/** Closes the popover. Equivalent to `setOpen(false)`. */
close: () => void
displayLabel: string
inputValue: string
}
/** Props bound to the `actions` slot on `DatePicker`. */
export interface DatePickerActionsSlotProps {
selected: string
setDate: (date: string | Date | Dayjs) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
/** Props bound to the `actions` slot on `DateRangePicker`. */
export interface DateRangePickerActionsSlotProps {
fromDate: string
toDate: string
/**
* Commits one endpoint, mirroring a calendar cell click:
* first call sets `from`, second call sets `to`.
*/
setDate: (date: string | Date | Dayjs) => void
/**
* Commits both endpoints atomically. Normalizes order so the earlier
* date becomes `from`. Use for fixed-window presets ("Last 7 days").
*/
setRange: (range: [string | Date | Dayjs, string | Date | Dayjs]) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
/** Props bound to the `actions` slot on `DateTimePicker`. */
export interface DateTimePickerActionsSlotProps {
selected: string
time: string
setDate: (date: string | Date | Dayjs) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
export interface DatePickerSlots {
/** Custom trigger renderer for the picker. */
trigger?: (props: DatePickerTriggerSlotProps) => any
/** Content rendered before the trigger input value. */
prefix?: (props: DatePickerTriggerSlotProps) => any
/** Content rendered after the trigger input value. */
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for date shortcuts
* ("Today", "Tomorrow", "Last 7 days") and other preset actions. When
* omitted, the popover shows the calendar only.
*/
actions?: (props: DatePickerActionsSlotProps) => any
}
export interface DateRangePickerSlots {
trigger?: (props: DatePickerTriggerSlotProps) => any
prefix?: (props: DatePickerTriggerSlotProps) => any
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for range shortcuts
* ("Last 7 days", "Last 12 months") and other preset actions. When
* omitted, the popover shows the calendar only.
*/
actions?: (props: DateRangePickerActionsSlotProps) => any
}
export interface DateTimePickerSlots {
trigger?: (props: DatePickerTriggerSlotProps) => any
prefix?: (props: DatePickerTriggerSlotProps) => any
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for date-time
* shortcuts ("Now", "Tomorrow 9am") and other preset actions. When
* omitted, the popover shows the calendar and time picker only.
*/
actions?: (props: DateTimePickerActionsSlotProps) => any
}
export type DatePickerViewMode = 'date' | 'monthYear'
export interface DatePickerDateObj {
date: Dayjs
key: string
inMonth: boolean
isToday: boolean
isSelected: boolean
}Preferred popover side relative to the trigger.
Alignment of the popover along the trigger edge.
Gap between the trigger and popover content in pixels.
Display format used for the input text.
Size of the trigger input.
Visual style variant passed through to the input.
Placeholder text shown when no value is selected.
Controls popover open state (for controlled usage).
Opens the popover when the input receives focus. Default: false.
Opens the popover when the input is clicked. Default: true.
Whether the trigger input accepts typed input. When `false` the user can still open the popover and pick a date, but cannot type a date manually. Default: `true`.
Disables the trigger input and calendar interactions.
Shows clear and quick-action controls when enabled.
Keeps the popover open after a date is selected. Default: false.
Earliest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`, `YYYY-MM-DD HH:mm:ss` for second-level granularity).
Latest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`, `YYYY-MM-DD HH:mm:ss` for second-level granularity).
Return true to prevent a date from being selected. Combined with `min`/`max`.
Controlled value for the picker.
Label rendered above (or beside, for binary controls) the input.
Helper text rendered below the input. Hidden when `error` is set. A `#description` slot is not: it renders beside the error, and is referenced alongside it.
Error message rendered below the input. When set, the control receives `aria-invalid="true"` and `data-state="invalid"`. Takes a string, an array of strings, or an `Error` whose `messages` are rendered as stacked lines (with `Error.message` as the fallback). This is the same value `ErrorMessage.message` takes. An empty array and an empty string both mean no error.
Marks the field as required. Renders an asterisk next to the label, with `sr-only` text that announces it, and forwards `required` / `aria-required` to the underlying control where the control's role allows it. `data-required` is set either way.
HTML id of the underlying control. Auto-generated via `useId()` if omitted.
| Slot | Payload |
|---|---|
trigger | DatePickerTriggerSlotProps Custom trigger renderer for the picker. |
prefix | DatePickerTriggerSlotProps Content rendered before the trigger input value. |
suffix | DatePickerTriggerSlotProps Content rendered after the trigger input value. |
actions | DatePickerActionsSlotProps Sidebar rendered to the left of the calendar. Use for date shortcuts ("Today", "Tomorrow", "Last 7 days") and other preset actions. When omitted, the popover shows the calendar only. |
Custom trigger renderer for the picker.
Content rendered before the trigger input value.
Content rendered after the trigger input value.
Sidebar rendered to the left of the calendar. Use for date shortcuts ("Today", "Tomorrow", "Last 7 days") and other preset actions. When omitted, the popover shows the calendar only.
| Event | Payload |
|---|---|
update:open | [value: boolean] Fired when the open state changes. |
change | [value: string] Fired after the value is committed. |
update:modelValue | [value: string] Fired when the model value changes. |
Fired when the open state changes.
Fired after the value is committed.
Fired when the model value changes.
| Template ref | Type |
|---|---|
open | () => void Opens the panel. A disabled picker stays closed. |
close | () => void Closes the panel. |
focus | (options?: FocusOptions) => void Moves focus to the input. With a custom `#trigger`, it focuses the first focusable element inside it. |
Opens the panel. A disabled picker stays closed.
Closes the panel.
Moves focus to the input. With a custom `#trigger`, it focuses the first focusable element inside it.
DateRangePicker
Show types
import type { Dayjs } from 'dayjs/esm'
import type { InputSize, InputVariant } from '../../composables/inputTypes'
import type { InputLabelingProps } from '../../composables/useInputLabeling'
// Re-exported, not redeclared: `index.ts` does `export * from './types'`, so a
// local copy publishes a second structurally identical `PopoverSide` at the
// package root that can drift from `Popover`'s without a compile error.
import type { PopoverAlign, PopoverSide } from '../Popover/types'
export type { PopoverAlign, PopoverSide }
// Shared props for both single date and range pickers
export interface CommonDatePickerProps extends InputLabelingProps {
// Positioning — aligned with Combobox/Dropdown vocabulary
/** Preferred popover side relative to the trigger. */
side?: PopoverSide
/** Alignment of the popover along the trigger edge. */
align?: PopoverAlign
/** Gap between the trigger and popover content in pixels. */
offset?: number
// Display
/** Display format used for the input text. */
format?: string
/** Size of the trigger input. */
size?: InputSize
/** Visual style variant passed through to the input. */
variant?: InputVariant
/** Placeholder text shown when no value is selected. */
placeholder?: string
// Interaction
/** Controls popover open state (for controlled usage). */
open?: boolean
/** Opens the popover when the input receives focus. Default: false. */
openOnFocus?: boolean
/** Opens the popover when the input is clicked. Default: true. */
openOnClick?: boolean
/**
* Whether the trigger input accepts typed input. When `false` the user can
* still open the popover and pick a date, but cannot type a date manually.
* Default: `true`.
*/
typeable?: boolean
/** Disables the trigger input and calendar interactions. */
disabled?: boolean
/** Shows clear and quick-action controls when enabled. */
clearable?: boolean
/** Keeps the popover open after a date is selected. Default: false. */
keepOpen?: boolean
// Constraints
/**
* Earliest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`,
* `YYYY-MM-DD HH:mm:ss` for second-level granularity).
*/
min?: string
/**
* Latest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`,
* `YYYY-MM-DD HH:mm:ss` for second-level granularity).
*/
max?: string
/** Return true to prevent a date from being selected. Combined with `min`/`max`. */
isDateUnavailable?: (date: Dayjs) => boolean
}
export interface DatePickerProps extends CommonDatePickerProps {
/** Controlled value for the picker. */
modelValue?: string
}
/** A range value: a `[from, to]` tuple in `YYYY-MM-DD` format, or `[]` when cleared. */
export type DateRangeValue = [string, string] | []
export interface DateRangePickerProps extends CommonDatePickerProps {
/**
* Controlled range value as `[from, to]` in `YYYY-MM-DD` format, or `[]` for
* no selection. `DateRangeValue` types both sides of `v-model` (INP-Q12): the
* prop was `string[]`, which let a one-element array in and made a round-trip
* through the model fail to type-check.
*/
modelValue?: DateRangeValue
/** Render two calendar panels side by side (current month + next month). */
dualPane?: boolean
}
export type DatePickerEmits = {
/** Fired when the picker value changes. */
(event: 'update:modelValue', value: string): void
/** Fired when the popover open state changes. */
(event: 'update:open', value: boolean): void
/** Fired after the picker commits a normalized value. */
(event: 'change', value: string): void
}
export type DateRangePickerEmits = {
/** Fired when the range value changes. Emits `[from, to]` or `[]` when cleared. */
(event: 'update:modelValue', value: DateRangeValue): void
/** Fired when the popover open state changes. */
(event: 'update:open', value: boolean): void
/** Fired after the picker commits a normalized range value. */
(event: 'change', value: DateRangeValue): void
}
export interface DateTimePickerProps extends CommonDatePickerProps {
/** Controlled value for the picker. */
modelValue?: string
}
export type DateTimePickerEmits = DatePickerEmits
// ── Slot prop shapes ─────────────────────────────────────────────────────────
/** Props bound to the trigger / prefix / suffix slots on all three pickers. */
export interface DatePickerTriggerSlotProps {
/** Whether the popover is currently open. */
open: boolean
/** Whether the picker is disabled. */
disabled: boolean
/** Sets the popover open state. */
setOpen: (value: boolean) => void
/** Closes the popover. Equivalent to `setOpen(false)`. */
close: () => void
displayLabel: string
inputValue: string
}
/** Props bound to the `actions` slot on `DatePicker`. */
export interface DatePickerActionsSlotProps {
selected: string
setDate: (date: string | Date | Dayjs) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
/** Props bound to the `actions` slot on `DateRangePicker`. */
export interface DateRangePickerActionsSlotProps {
fromDate: string
toDate: string
/**
* Commits one endpoint, mirroring a calendar cell click:
* first call sets `from`, second call sets `to`.
*/
setDate: (date: string | Date | Dayjs) => void
/**
* Commits both endpoints atomically. Normalizes order so the earlier
* date becomes `from`. Use for fixed-window presets ("Last 7 days").
*/
setRange: (range: [string | Date | Dayjs, string | Date | Dayjs]) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
/** Props bound to the `actions` slot on `DateTimePicker`. */
export interface DateTimePickerActionsSlotProps {
selected: string
time: string
setDate: (date: string | Date | Dayjs) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
export interface DatePickerSlots {
/** Custom trigger renderer for the picker. */
trigger?: (props: DatePickerTriggerSlotProps) => any
/** Content rendered before the trigger input value. */
prefix?: (props: DatePickerTriggerSlotProps) => any
/** Content rendered after the trigger input value. */
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for date shortcuts
* ("Today", "Tomorrow", "Last 7 days") and other preset actions. When
* omitted, the popover shows the calendar only.
*/
actions?: (props: DatePickerActionsSlotProps) => any
}
export interface DateRangePickerSlots {
trigger?: (props: DatePickerTriggerSlotProps) => any
prefix?: (props: DatePickerTriggerSlotProps) => any
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for range shortcuts
* ("Last 7 days", "Last 12 months") and other preset actions. When
* omitted, the popover shows the calendar only.
*/
actions?: (props: DateRangePickerActionsSlotProps) => any
}
export interface DateTimePickerSlots {
trigger?: (props: DatePickerTriggerSlotProps) => any
prefix?: (props: DatePickerTriggerSlotProps) => any
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for date-time
* shortcuts ("Now", "Tomorrow 9am") and other preset actions. When
* omitted, the popover shows the calendar and time picker only.
*/
actions?: (props: DateTimePickerActionsSlotProps) => any
}
export type DatePickerViewMode = 'date' | 'monthYear'
export interface DatePickerDateObj {
date: Dayjs
key: string
inMonth: boolean
isToday: boolean
isSelected: boolean
}Preferred popover side relative to the trigger.
Alignment of the popover along the trigger edge.
Gap between the trigger and popover content in pixels.
Display format used for the input text.
Size of the trigger input.
Visual style variant passed through to the input.
Placeholder text shown when no value is selected.
Controls popover open state (for controlled usage).
Opens the popover when the input receives focus. Default: false.
Opens the popover when the input is clicked. Default: true.
Whether the trigger input accepts typed input. When `false` the user can still open the popover and pick a date, but cannot type a date manually. Default: `true`.
Disables the trigger input and calendar interactions.
Shows clear and quick-action controls when enabled.
Keeps the popover open after a date is selected. Default: false.
Earliest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`, `YYYY-MM-DD HH:mm:ss` for second-level granularity).
Latest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`, `YYYY-MM-DD HH:mm:ss` for second-level granularity).
Return true to prevent a date from being selected. Combined with `min`/`max`.
Controlled range value as `[from, to]` in `YYYY-MM-DD` format, or `[]` for no selection. `DateRangeValue` types both sides of `v-model` (INP-Q12): the prop was `string[]`, which let a one-element array in and made a round-trip through the model fail to type-check.
Render two calendar panels side by side (current month + next month).
Label rendered above (or beside, for binary controls) the input.
Helper text rendered below the input. Hidden when `error` is set. A `#description` slot is not: it renders beside the error, and is referenced alongside it.
Error message rendered below the input. When set, the control receives `aria-invalid="true"` and `data-state="invalid"`. Takes a string, an array of strings, or an `Error` whose `messages` are rendered as stacked lines (with `Error.message` as the fallback). This is the same value `ErrorMessage.message` takes. An empty array and an empty string both mean no error.
Marks the field as required. Renders an asterisk next to the label, with `sr-only` text that announces it, and forwards `required` / `aria-required` to the underlying control where the control's role allows it. `data-required` is set either way.
HTML id of the underlying control. Auto-generated via `useId()` if omitted.
| Slot | Payload |
|---|---|
trigger | DatePickerTriggerSlotProps |
prefix | DatePickerTriggerSlotProps |
suffix | DatePickerTriggerSlotProps |
actions | DateRangePickerActionsSlotProps Sidebar rendered to the left of the calendar. Use for range shortcuts ("Last 7 days", "Last 12 months") and other preset actions. When omitted, the popover shows the calendar only. |
Sidebar rendered to the left of the calendar. Use for range shortcuts ("Last 7 days", "Last 12 months") and other preset actions. When omitted, the popover shows the calendar only.
| Event | Payload |
|---|---|
update:open | [value: boolean] Fired when the open state changes. |
change | [value: DateRangeValue] Fired after the value is committed. |
update:modelValue | [value: DateRangeValue] Fired when the model value changes. |
Fired when the open state changes.
Fired after the value is committed.
Fired when the model value changes.
| Template ref | Type |
|---|---|
open | () => void Opens the panel. A disabled picker stays closed. |
close | () => void Closes the panel. |
focus | (options?: FocusOptions) => void Moves focus to the input. With a custom `#trigger`, it focuses the first focusable element inside it. |
Opens the panel. A disabled picker stays closed.
Closes the panel.
Moves focus to the input. With a custom `#trigger`, it focuses the first focusable element inside it.
DateTimePicker
Show types
import type { Dayjs } from 'dayjs/esm'
import type { InputSize, InputVariant } from '../../composables/inputTypes'
import type { InputLabelingProps } from '../../composables/useInputLabeling'
// Re-exported, not redeclared: `index.ts` does `export * from './types'`, so a
// local copy publishes a second structurally identical `PopoverSide` at the
// package root that can drift from `Popover`'s without a compile error.
import type { PopoverAlign, PopoverSide } from '../Popover/types'
export type { PopoverAlign, PopoverSide }
// Shared props for both single date and range pickers
export interface CommonDatePickerProps extends InputLabelingProps {
// Positioning — aligned with Combobox/Dropdown vocabulary
/** Preferred popover side relative to the trigger. */
side?: PopoverSide
/** Alignment of the popover along the trigger edge. */
align?: PopoverAlign
/** Gap between the trigger and popover content in pixels. */
offset?: number
// Display
/** Display format used for the input text. */
format?: string
/** Size of the trigger input. */
size?: InputSize
/** Visual style variant passed through to the input. */
variant?: InputVariant
/** Placeholder text shown when no value is selected. */
placeholder?: string
// Interaction
/** Controls popover open state (for controlled usage). */
open?: boolean
/** Opens the popover when the input receives focus. Default: false. */
openOnFocus?: boolean
/** Opens the popover when the input is clicked. Default: true. */
openOnClick?: boolean
/**
* Whether the trigger input accepts typed input. When `false` the user can
* still open the popover and pick a date, but cannot type a date manually.
* Default: `true`.
*/
typeable?: boolean
/** Disables the trigger input and calendar interactions. */
disabled?: boolean
/** Shows clear and quick-action controls when enabled. */
clearable?: boolean
/** Keeps the popover open after a date is selected. Default: false. */
keepOpen?: boolean
// Constraints
/**
* Earliest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`,
* `YYYY-MM-DD HH:mm:ss` for second-level granularity).
*/
min?: string
/**
* Latest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`,
* `YYYY-MM-DD HH:mm:ss` for second-level granularity).
*/
max?: string
/** Return true to prevent a date from being selected. Combined with `min`/`max`. */
isDateUnavailable?: (date: Dayjs) => boolean
}
export interface DatePickerProps extends CommonDatePickerProps {
/** Controlled value for the picker. */
modelValue?: string
}
/** A range value: a `[from, to]` tuple in `YYYY-MM-DD` format, or `[]` when cleared. */
export type DateRangeValue = [string, string] | []
export interface DateRangePickerProps extends CommonDatePickerProps {
/**
* Controlled range value as `[from, to]` in `YYYY-MM-DD` format, or `[]` for
* no selection. `DateRangeValue` types both sides of `v-model` (INP-Q12): the
* prop was `string[]`, which let a one-element array in and made a round-trip
* through the model fail to type-check.
*/
modelValue?: DateRangeValue
/** Render two calendar panels side by side (current month + next month). */
dualPane?: boolean
}
export type DatePickerEmits = {
/** Fired when the picker value changes. */
(event: 'update:modelValue', value: string): void
/** Fired when the popover open state changes. */
(event: 'update:open', value: boolean): void
/** Fired after the picker commits a normalized value. */
(event: 'change', value: string): void
}
export type DateRangePickerEmits = {
/** Fired when the range value changes. Emits `[from, to]` or `[]` when cleared. */
(event: 'update:modelValue', value: DateRangeValue): void
/** Fired when the popover open state changes. */
(event: 'update:open', value: boolean): void
/** Fired after the picker commits a normalized range value. */
(event: 'change', value: DateRangeValue): void
}
export interface DateTimePickerProps extends CommonDatePickerProps {
/** Controlled value for the picker. */
modelValue?: string
}
export type DateTimePickerEmits = DatePickerEmits
// ── Slot prop shapes ─────────────────────────────────────────────────────────
/** Props bound to the trigger / prefix / suffix slots on all three pickers. */
export interface DatePickerTriggerSlotProps {
/** Whether the popover is currently open. */
open: boolean
/** Whether the picker is disabled. */
disabled: boolean
/** Sets the popover open state. */
setOpen: (value: boolean) => void
/** Closes the popover. Equivalent to `setOpen(false)`. */
close: () => void
displayLabel: string
inputValue: string
}
/** Props bound to the `actions` slot on `DatePicker`. */
export interface DatePickerActionsSlotProps {
selected: string
setDate: (date: string | Date | Dayjs) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
/** Props bound to the `actions` slot on `DateRangePicker`. */
export interface DateRangePickerActionsSlotProps {
fromDate: string
toDate: string
/**
* Commits one endpoint, mirroring a calendar cell click:
* first call sets `from`, second call sets `to`.
*/
setDate: (date: string | Date | Dayjs) => void
/**
* Commits both endpoints atomically. Normalizes order so the earlier
* date becomes `from`. Use for fixed-window presets ("Last 7 days").
*/
setRange: (range: [string | Date | Dayjs, string | Date | Dayjs]) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
/** Props bound to the `actions` slot on `DateTimePicker`. */
export interface DateTimePickerActionsSlotProps {
selected: string
time: string
setDate: (date: string | Date | Dayjs) => void
clear: () => void
close: () => void
open: boolean
disabled: boolean
setOpen: (value: boolean) => void
}
export interface DatePickerSlots {
/** Custom trigger renderer for the picker. */
trigger?: (props: DatePickerTriggerSlotProps) => any
/** Content rendered before the trigger input value. */
prefix?: (props: DatePickerTriggerSlotProps) => any
/** Content rendered after the trigger input value. */
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for date shortcuts
* ("Today", "Tomorrow", "Last 7 days") and other preset actions. When
* omitted, the popover shows the calendar only.
*/
actions?: (props: DatePickerActionsSlotProps) => any
}
export interface DateRangePickerSlots {
trigger?: (props: DatePickerTriggerSlotProps) => any
prefix?: (props: DatePickerTriggerSlotProps) => any
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for range shortcuts
* ("Last 7 days", "Last 12 months") and other preset actions. When
* omitted, the popover shows the calendar only.
*/
actions?: (props: DateRangePickerActionsSlotProps) => any
}
export interface DateTimePickerSlots {
trigger?: (props: DatePickerTriggerSlotProps) => any
prefix?: (props: DatePickerTriggerSlotProps) => any
suffix?: (props: DatePickerTriggerSlotProps) => any
/**
* Sidebar rendered to the left of the calendar. Use for date-time
* shortcuts ("Now", "Tomorrow 9am") and other preset actions. When
* omitted, the popover shows the calendar and time picker only.
*/
actions?: (props: DateTimePickerActionsSlotProps) => any
}
export type DatePickerViewMode = 'date' | 'monthYear'
export interface DatePickerDateObj {
date: Dayjs
key: string
inMonth: boolean
isToday: boolean
isSelected: boolean
}Preferred popover side relative to the trigger.
Alignment of the popover along the trigger edge.
Gap between the trigger and popover content in pixels.
Display format used for the input text.
Size of the trigger input.
Visual style variant passed through to the input.
Placeholder text shown when no value is selected.
Controls popover open state (for controlled usage).
Opens the popover when the input receives focus. Default: false.
Opens the popover when the input is clicked. Default: true.
Whether the trigger input accepts typed input. When `false` the user can still open the popover and pick a date, but cannot type a date manually. Default: `true`.
Disables the trigger input and calendar interactions.
Shows clear and quick-action controls when enabled.
Keeps the popover open after a date is selected. Default: false.
Earliest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`, `YYYY-MM-DD HH:mm:ss` for second-level granularity).
Latest selectable date. Accepts `YYYY-MM-DD` (or, for `DateTimePicker`, `YYYY-MM-DD HH:mm:ss` for second-level granularity).
Return true to prevent a date from being selected. Combined with `min`/`max`.
Controlled value for the picker.
Label rendered above (or beside, for binary controls) the input.
Helper text rendered below the input. Hidden when `error` is set. A `#description` slot is not: it renders beside the error, and is referenced alongside it.
Error message rendered below the input. When set, the control receives `aria-invalid="true"` and `data-state="invalid"`. Takes a string, an array of strings, or an `Error` whose `messages` are rendered as stacked lines (with `Error.message` as the fallback). This is the same value `ErrorMessage.message` takes. An empty array and an empty string both mean no error.
Marks the field as required. Renders an asterisk next to the label, with `sr-only` text that announces it, and forwards `required` / `aria-required` to the underlying control where the control's role allows it. `data-required` is set either way.
HTML id of the underlying control. Auto-generated via `useId()` if omitted.
| Slot | Payload |
|---|---|
trigger | DatePickerTriggerSlotProps |
prefix | DatePickerTriggerSlotProps |
suffix | DatePickerTriggerSlotProps |
actions | DateTimePickerActionsSlotProps Sidebar rendered to the left of the calendar. Use for date-time shortcuts ("Now", "Tomorrow 9am") and other preset actions. When omitted, the popover shows the calendar and time picker only. |
Sidebar rendered to the left of the calendar. Use for date-time shortcuts ("Now", "Tomorrow 9am") and other preset actions. When omitted, the popover shows the calendar and time picker only.
| Event | Payload |
|---|---|
update:open | [value: boolean] Fired when the open state changes. |
change | [value: string] Fired after the value is committed. |
update:modelValue | [value: string] Fired when the model value changes. |
Fired when the open state changes.
Fired after the value is committed.
Fired when the model value changes.
| Template ref | Type |
|---|---|
open | () => void Opens the panel. A disabled picker stays closed. |
close | () => void Closes the panel. |
focus | (options?: FocusOptions) => void Moves focus to the input. With a custom `#trigger`, it focuses the first focusable element inside it. |
Opens the panel. A disabled picker stays closed.
Closes the panel.
Moves focus to the input. With a custom `#trigger`, it focuses the first focusable element inside it.