DateRangePicker
Overview
A date range picker that pairs a keyboard-editable dual segmented date range field with a calendar range popup for visual selection. Type a start and end date directly into the segments or click the calendar toggle and pick both ends from a single month-range grid. Use it whenever a user must choose a bounded interval — booking stay dates, reporting windows, or filter ranges. For pure typing with no popup, prefer SDateRangeField; for a single date with a popup, use SDatePicker.
Usage
Features
- 📅 Dual field + popup combo — the segmented
DateRangeFieldstays editable while aCalendarRangepopover offers visual selection of both dates - 🎚 Controlled / uncontrolled —
v-model/defaultValuefor the value plusopen/defaultOpenfor the popup, both backed byuseControllableState - ⌨️ Full keyboard editing — segment increment/decrement/typing in both groups, cross-group arrow movement, arrow-key grid navigation in the calendar,
Escapecloses - ⛓ Range validation —
minValue/maxValue/isDateUnavailabledisable out-of-range and unavailable days in both the field and the calendar - 🌍 Locale-aware accessibility — default
aria-labels for the calendar toggle and popup dialog come from the active locale messages and are overridable viatriggerProps/popupProps - 🧩 Composable slots —
leadingfor content before the segments andseparatorfor a custom divider; the default slot exposesopen/close/calendarRangePropsfor a fully custom popup - 🎨 Fine-grained styling —
uicovers the field plustrigger/positioner/popupslots, andcalendarRangeUitargets the embedded calendar independently - 🚫 Disabled state —
disabledblocks both field editing and popup opening
Component family
SDateRangePicker— the styled wrapper that forwards props to the Aria compact, injectsdateRangePickerVariants(extendingdateFieldVariants), and rendersSCalendarRangeinside the popupDateRangePickerCompact(Aria) — data-driven composition ofDateRangeFieldCompact+PopoverCompact(calendar-icon trigger) that exposes the popup state andcalendarRangePropsthrough its default slot; import from@vean/aria/date-range-pickerfor unstyled usageDateRangeFieldCompact/PopoverCompact/CalendarRangeCompact(Aria) — the dual segmented field, the popover mechanics, and the range calendar grid that back the picker
Demos
Basic
Selected: Sun Apr 19 2026 00:00:00 GMT+0000 (Coordinated Universal Time) to Sun Apr 26 2026 00:00:00 GMT+0000 (Coordinated Universal Time)
Custom Styling
API
Aria Layer
2DateRangePicker
Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | ClassValue | — | Additional class names applied to the root element. |
size | ThemeSize | — | Visual size of the component. |
ui | Partial<DateRangePickerUi> | — | Per-slot class overrides for the component. |
calendarRangeUi | Partial<CalendarRangeUi> | — | Per-slot class overrides for the CalendarRange component within the DateRangePicker. |
dateFieldProps | BaseProps | — | — |
placement | import("@/index").Placement | — | Placement. |
showArrow | boolean | — | Whether to show an arrow. |
triggerProps | PopoverTriggerProps | — | Properties forwarded to the trigger element. |
portalProps | PopperPortalProps | — | Properties forwarded to the portal element. |
positionerProps | PopoverPositionerProps | — | Properties forwarded to the positioner element. |
popupProps | PopoverPopupProps | — | Properties forwarded to the popup element. |
arrowProps | PopperArrowProps | — | Properties forwarded to the arrow element. |
closeProps | PopoverCloseProps | — | Properties forwarded to the close element. |
dir | Direction | — | Reading direction of the component. |
open | boolean | — | — |
defaultOpen | boolean | — | — |
modal | boolean | — | — |
disabled | boolean | — | Whether the component is disabled. |
headerProps | CalendarRangeHeaderProps | — | Properties forwarded to the header element. |
headingProps | CalendarRangeHeadingProps | — | Properties forwarded to the heading element. |
prevProps | CalendarRangePrevProps | — | Properties forwarded to the prev element. |
nextProps | CalendarRangeNextProps | — | Properties forwarded to the next element. |
gridProps | CalendarRangeGridProps | — | Properties forwarded to the grid element. |
gridHeadProps | CalendarRangeGridHeadProps | — | Properties forwarded to the grid head element. |
gridBodyProps | CalendarRangeGridBodyProps | — | Properties forwarded to the grid body element. |
gridRowProps | CalendarRangeGridRowProps | — | Properties forwarded to the grid row element. |
headCellProps | CalendarRangeHeadCellProps | — | Properties forwarded to the head cell element. |
cellProps | CalendarRangeCellProps | — | Properties forwarded to the cell element. |
cellTriggerProps | CalendarRangeCellTriggerProps | — | Properties forwarded to the cell trigger element. |
defaultValue | DateRange | — | Default value. |
defaultPlaceholder | DateValue | — | Default placeholder. |
placeholder | DateValue | — | Placeholder. |
modelValue | DateRange | — | Current model value. |
allowNonContiguousRanges | boolean | — | Whether to allow non contiguous ranges. |
pagedNavigation | boolean | — | Whether paged navigation. |
preventDeselect | boolean | — | Whether prevent deselect. |
maximumDays | number | — | Maximum days. |
weekStartsOn | WeekStartsOn | — | Week starts on. |
weekdayFormat | WeekDayFormat | — | Weekday format. |
calendarLabel | string | — | Calendar label. |
fixedWeeks | boolean | — | Whether fixed weeks. |
maxValue | DateValue | — | Max value. |
minValue | DateValue | — | Min value. |
locale | string | — | Locale. |
numberOfMonths | number | — | Number of months. |
readonly | boolean | — | Whether the component is readonly. |
initialFocus | boolean | — | Whether initial focus. |
isDateDisabled | DateMatcher | — | Whether the date is disabled. |
isDateUnavailable | DateMatcher | — | Whether the date is unavailable. |
isDateHighlightable | DateMatcher | — | Whether a date highlightable. |
nextPage | ((placeholder: DateValue) => DateValue) | — | Next page. |
prevPage | ((placeholder: DateValue) => DateValue) | — | Prev page. |
disableDaysOutsideCurrentView | boolean | — | Whether to disable days outside current view. |
fixedDate | 'start' | 'end' | — | Fixed date. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean | Event handler called when the open state of the dialog changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
placed | — | — |
update:modelValue | DateRange | Emitted when the model value changes. |
update:placeholder | DateValue | Emitted when the placeholder value changes. |
update:startValue | DateValue | null | Emitted when the start value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
leading | — | Custom content rendered before the date range segments. |
separator | — | Custom content rendered between the start and end segment groups. |
DateRangePickerCompact
Props
| Prop | Type | Default | Description |
|---|---|---|---|
dateFieldProps | BaseProps | — | — |
placement | import("@/index").Placement | — | Placement. |
showArrow | boolean | — | Whether to show an arrow. |
triggerProps | PopoverTriggerProps | — | Properties forwarded to the trigger element. |
portalProps | PopperPortalProps | — | Properties forwarded to the portal element. |
positionerProps | PopoverPositionerProps | — | Properties forwarded to the positioner element. |
popupProps | PopoverPopupProps | — | Properties forwarded to the popup element. |
arrowProps | PopperArrowProps | — | Properties forwarded to the arrow element. |
closeProps | PopoverCloseProps | — | Properties forwarded to the close element. |
dir | Direction | — | Reading direction of the component. |
open | boolean | — | — |
defaultOpen | boolean | — | — |
modal | boolean | — | — |
disabled | boolean | — | Whether the component is disabled. |
headerProps | CalendarRangeHeaderProps | — | Properties forwarded to the header element. |
headingProps | CalendarRangeHeadingProps | — | Properties forwarded to the heading element. |
prevProps | CalendarRangePrevProps | — | Properties forwarded to the prev element. |
nextProps | CalendarRangeNextProps | — | Properties forwarded to the next element. |
gridProps | CalendarRangeGridProps | — | Properties forwarded to the grid element. |
gridHeadProps | CalendarRangeGridHeadProps | — | Properties forwarded to the grid head element. |
gridBodyProps | CalendarRangeGridBodyProps | — | Properties forwarded to the grid body element. |
gridRowProps | CalendarRangeGridRowProps | — | Properties forwarded to the grid row element. |
headCellProps | CalendarRangeHeadCellProps | — | Properties forwarded to the head cell element. |
cellProps | CalendarRangeCellProps | — | Properties forwarded to the cell element. |
cellTriggerProps | CalendarRangeCellTriggerProps | — | Properties forwarded to the cell trigger element. |
defaultValue | DateRange | — | Default value. |
defaultPlaceholder | DateValue | — | Default placeholder. |
placeholder | DateValue | — | Placeholder. |
modelValue | DateRange | — | Current model value. |
allowNonContiguousRanges | boolean | — | Whether to allow non contiguous ranges. |
pagedNavigation | boolean | — | Whether paged navigation. |
preventDeselect | boolean | — | Whether prevent deselect. |
maximumDays | number | — | Maximum days. |
weekStartsOn | WeekStartsOn | — | Week starts on. |
weekdayFormat | WeekDayFormat | — | Weekday format. |
calendarLabel | string | — | Calendar label. |
fixedWeeks | boolean | — | Whether fixed weeks. |
maxValue | DateValue | — | Max value. |
minValue | DateValue | — | Min value. |
locale | string | — | Locale. |
numberOfMonths | number | — | Number of months. |
readonly | boolean | — | Whether the component is readonly. |
initialFocus | boolean | — | Whether initial focus. |
isDateDisabled | DateMatcher | — | Whether the date is disabled. |
isDateUnavailable | DateMatcher | — | Whether the date is unavailable. |
isDateHighlightable | DateMatcher | — | Whether a date highlightable. |
nextPage | ((placeholder: DateValue) => DateValue) | — | Next page. |
prevPage | ((placeholder: DateValue) => DateValue) | — | Prev page. |
disableDaysOutsideCurrentView | boolean | — | Whether to disable days outside current view. |
fixedDate | 'start' | 'end' | — | Fixed date. |
asChild | boolean | — | Change the default rendered element for the one passed as a child, merging their props and behavior. |
as | AsTag | Component | 'div' | The element or component this component should render as. Can be overwrite by `asChild` |
Emits
| Emit Name | Parameters | Description |
|---|---|---|
update:open | boolean | Event handler called when the open state of the dialog changes. |
escapeKeyDown | KeyboardEvent | Event handler called when the escape key is down. Can be prevented. |
pointerDownOutside | PointerDownOutsideEvent | Event handler called when a `pointerdown` event happens outside of the `DismissableLayer`. Can be prevented. |
focusOutside | FocusOutsideEvent | Event handler called when the focus moves outside of the `DismissableLayer`. Can be prevented. |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | Event handler called when an interaction happens outside the `DismissableLayer`. Specifically, when a `pointerdown`
event happens outside or focus moves outside of it. Can be prevented. |
openAutoFocus | Event | Event handler called when auto-focusing on open. Can be prevented. |
closeAutoFocus | Event | Event handler called when auto-focusing on close. Can be prevented. |
placed | — | — |
update:modelValue | DateRange | Emitted when the model value changes. |
update:placeholder | DateValue | Emitted when the placeholder value changes. |
update:startValue | DateValue | null | Emitted when the start value changes. |
Slots
| Slot Name | Parameters | Description |
|---|---|---|
leading | — | Custom content rendered before the date range segments. |
separator | — | Custom content rendered between the start and end segment groups. |
default | DateRangePickerCompactSlotProps | Custom content for the calendar range popup, receiving the open/close controls and calendar props. |
Notes
Architecture and benchmark differences
SDateRangePicker forwards props to DateRangePickerCompact, which wires DateRangeFieldCompact (dual segments + validation) and PopoverCompact (a lucide:calendar icon trigger with aria-haspopup="dialog"). The styled layer injects dateRangePickerVariants classes through provideDateRangePickerUi so the nested popover parts get trigger/positioner/popup styling, then renders SCalendarRange in the default slot with the forwarded calendarRangeProps. Selecting a start and an end day emits update:modelValue and closes the popup once both ends are set. The popup is a role="dialog" with a default accessible name from the locale popupLabel message; the toggle gets toggle. Most benchmark libraries ship two plain text inputs plus a range panel; the dual-segmented-field-plus-popover combo, the cross-group keyboard focus, and the Aria/styled split are the differentiators.
| Capability | VeanUI | Ant Design | Element Plus | Mantine | Naive UI | shadcn |
|---|---|---|---|---|---|---|
| Aria/styled split | ✅ | — | — | — | — | — |
| Dual segmented field + range popup | ✅ | — | — | — | — | — |
| Controlled / uncontrolled value | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Controlled / uncontrolled open | ✅ | — | — | — | — | — |
| Keyboard segment editing | ✅ | — | — | — | — | — |
| Cross-group focus movement | ✅ | — | — | — | — | — |
| Calendar keyboard navigation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Range validation | ✅ | ✅ | ✅ | ✅ | ✅ | — |
isDateUnavailable | ✅ | — | — | — | — | — |
| Disabled state | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| Locale-driven accessible names | ✅ | — | — | — | — | — |
leading / separator slots | ✅ | — | — | — | — | — |
Independent calendarRangeUi | ✅ | — | — | — | — | — |
Cautions
- The value is a
DateRange— an object{ start, end }ofDateValues, not astring. Use it withSDateRangeField,SCalendarRange, and the other date-family components. defaultValue/defaultOpenare only read on mount — usev-model/openfor external control.- The toggle button is the calendar icon; its default
aria-label(localetoggle) can be overridden throughtriggerProps['aria-label']. The icon itself is fixed — to render a custom trigger, composeDateRangePickerCompactfrom@vean/aria/date-range-pickerwith your own popover content. - The popup
role="dialog"gets its accessible name from the localepopupLabelmessage; override it throughpopupProps['aria-label']. - Pass
dateFieldProps(e.g.placeholder,locale,granularity) to configure the embedded dual segmented field; field and calendar shareminValue/maxValue/isDateUnavailable. calendarRangeUiis consumed by the embedded calendar and never reaches the DOM — the same applies todateFieldProps.- A start later than end is marked invalid but the values are never swapped automatically — clear or correct them in the parent.
FAQ
How do I choose a date range with the keyboard?
Tab into either segmented group and type digits or use arrow keys, or press the calendar toggle and navigate the month grid with arrow keys, Home/End and PageUp/PageDown; Enter commits the selection, Escape closes without selecting. Both ends must be picked for the value to emit.
How do I restrict the selectable range?
Pass minValue/maxValue — out-of-range days are disabled in the calendar and the field marks itself data-invalid. isDateUnavailable accepts a predicate for arbitrary exclusions and applies to both the field and the calendar.
How do I configure the embedded date range field?
Use dateFieldProps — for example { placeholder }, { locale }, { granularity: 'minute' } or { hourCycle: 12 }. The field accepts the same props as SDateRangeField itself.
How do I customize the calendar look?
calendarRangeUi overrides the range calendar’s per-slot classes independently of ui. For deeper customization, the default slot receives calendarRangeProps (plus open/close) so you can render your own popup content with CalendarRangeCompact from @vean/aria/calendar-range.
How is SDateRangePicker different from SDateRangeField?
SDateRangeField is a pure dual segmented input with no popup — fastest for typing and form submission. SDateRangePicker adds the calendar range popover on top of the same field, trading a little footprint for visual selection.