ComponentsPickers

Date range picker

Pick a from–to range for report filters, leave requests, bookings and campaign flights. Presets column, two months side by side (one on phones), a hover preview of the band, min / max length, and an Apply mode for expensive queries. Values are YYYY-MM-DD strings.

react-day-picker v9 (range mode) + Radix Popover components/date-range-picker.tsx, components/calendar.tsx, components/date-picker.tsx, lib/date.ts, styles/calendar.csstheme · material · accent · density · font

2026-11-01 → 2026-11-10 (10 days)

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { DateRangePicker, type IsoDateRange } from "@vamos-ui/react";

const [range, setRange] = React.useState<IsoDateRange | null>(null);

<DateRangePicker aria-label="Report period" presets value={range} onValueChange={setRange} />

Presets are a listbox (↑ ↓ Home End, Enter). The grid uses the WAI-ARIA grid keyboard model. The selected range is announced: “From November 1, 2026 to November 10, 2026, 10 days”.

On glass, the range band is an opaque mix of the accent and the surface, so it never shows the page through it.

Examples

Apply to commit

commit="apply" holds the draft until Apply, so a dashboard runs one query per decision. Cancel or Esc discards it.

Revenue by channel

Gross revenue
$84,210
Report queries run
1

Length limits & booked days

After the first click, days that would make the range shorter than minDays or longer than maxDays are disabled. excludeDisabled restarts a range that would span a booked day.

2 to 14 nights. Greyed-out dates are already booked.

Custom presets

Any { id, label, range(today) } list — here fiscal quarters. The active preset is highlighted in the listbox.

Typeable fields

trigger="input": two fields with the same parser as Date Picker; an end before the start is swapped.

Type both dates or pick them from the calendar.

Sizes & states

Small, disabled, a Field error and empty.

The period overlaps an issued invoice.

API

DateRangePicker

PropTypeDefault
value / defaultValue
{ from, to? } as YYYY-MM-DD. to is missing while the end is being picked.
IsoDateRange | null—
onValueChange
Every change (immediate) or on Apply.
(value: IsoDateRange | null) => void—
presets
true = Today, Yesterday, Last 7 / 30 / 90 days, This week, This / Last month, This year. Week presets follow the locale's week start.
boolean | DateRangePreset[]false
numberOfMonths
Drops to 1 below the sm breakpoint.
1 | 22
commit
Immediate closes after the end is picked; apply waits for the button.
"immediate" | "apply""immediate"
minDays / maxDays
Inclusive length limits in days.
number—
min / max
Earliest / latest date (YYYY-MM-DD).
string—
disabledDays
Matchers or YYYY-MM-DD strings.
DayMatcher | DayMatcher[]—
excludeDisabled
A range may not span a disabled day.
booleanfalse
trigger
One trigger showing the range, or two typeable fields.
"button" | "input""button"
nameFrom / nameTo
Hidden inputs submitting YYYY-MM-DD.
string—
placeholder
Empty trigger text.
string"Pick a date range"
clearable
Clear in the footer (and in the input trigger).
booleanfalse
size / invalid / disabled / readOnly / required
As every control; also read from Field.
—
open / defaultOpen / onOpenChange
Popover state.
boolean / (open) => void—
calendarProps
Extra grid props (showWeekNumber, renderDayExtra…).
DatePickerCalendarProps—
labels
dateRangePicker namespace: preset names, Apply / Cancel, days plural, summary announcement.
Partial<DateRangePickerLabels>—

DateRangePreset

PropTypeDefault
id
Stable key.
string—
label
Shown in the presets listbox.
string—
range
Computed when the popover renders, from today in the provider's time zone.
(today: string, ctx: { weekStartsOn }) => { from: string; to: string }—