DateRangePicker
Range selection in two clicks — start and end date in one calendar popover, closing itself once both are set. Shares the DatePicker surface: same sizes, variants, constraints, and locale handling.
Playground
<DateRangePicker
label="Travel dates"
/>01 Examples
Booking a stay
closeOnSelect (the default) dismisses the popover once the range is complete, so the flow ends without a second gesture.<DateRangePicker
label="Check-in / check-out"
bind:value={stay}
placeholder="Pick your dates"
clearable
/>Bounded to a bookable window
minDate and maxDate fence the calendar; isDateDisabled handles the irregular gaps a fixed range cannot express — here every Sunday. A predicate that throws is caught and logged, and the date is then treated as allowed.<DateRangePicker
label="Bookable period"
bind:value={bookable}
minDate={TODAY}
maxDate={IN_90_DAYS}
isDateDisabled={(d) => d.getDay() === 0}
helper="Next 90 days, Sundays excluded"
/>Reporting period in a form
name renders two hidden inputs — period_start and period_end — each carrying the serialised date, so a native submit never sends the locale-formatted display string. An empty range submits both halves as an empty string.<DateRangePicker
label="Reporting period"
name="period"
bind:value={reportRange}
required
helper="Submitted as period_start and period_end"
/>02 Customization
The trigger and the calendar are styled separately: inputVariant takes the Input ladder (outlined, filled, ghost, underline) and calendarVariant the Calendar one (default, bordered, ghost). size drives both.
Everything the calendar itself understands passes straight through — locale, weekStartsOn, showWeekNumbers, showOutsideDays, fixedWeeks. locale defaults to 'auto', which follows the active <I18nProvider>, so an app that already declares
its language does not repeat it here.
For a single date use DatePicker; for the calendar surface without an input, see Calendar. See Customization for the general contract.
03 Accessibility
The trigger announces its popup
The text input carries aria-haspopup="dialog", aria-expanded, and — while open — aria-controls pointing at the calendar, so assistive tech
reports both that a calendar exists and whether it is showing.
Keyboard
↓ opens the calendar from the field. Enter closes it while open, and commits what has been typed while it is not. Esc closes the calendar; pressed again on a field with an uncommitted draft it discards that draft rather than the selection. Grid navigation inside the calendar follows the Calendar pattern.
Both embedded buttons are named
When clearable is set the field carries two controls,
and each gets its own localized aria-label — "clear input" and "open calendar" —
rather than sharing one ambiguous name. Both keep a visible focus-visible ring.
Typing is a first-class path
The range can be typed as well as clicked; parsing happens on blur or Enter, and a parse failure surfaces as the field's error. A picker that can only be operated by pointer
excludes anyone who is faster on the keyboard.
Dismissal is separable from notification
closeOnEscape and closeOnClickOutside govern whether the popover
closes; onEscape and onClickOutside only tell you it happened. Turning a notification
into the close mechanism is the usual way a picker ends up impossible to dismiss by keyboard.
04 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
calendarVariant | defaultborderedghost | — | calendarVariant property | |
class | string | — | Additional CSS classes to apply to the DateRangePicker component | |
clearable | boolean | — | Clearable property for the DateRangePicker component | |
closeOnClickOutside | boolean | true | Whether the popover closes on outside click. | |
closeOnEscape | boolean | true | Whether the popover closes on Escape key. | |
closeOnSelect | boolean | true | Close popover after selecting both dates. | |
defaultMonth | MonthIndex | — | Default month shown when the picker opens without a value. 0–11. | |
defaultYear | number | — | Default year shown when the picker opens without a value. | |
disabled | boolean | — | Whether the DateRangePicker is disabled and non-interactive | |
disabledDates | Date[] | — | disabledDates property | |
displayFormat | DateFormatOptions | — | Intl.DateTimeFormat options for displayed dates. | |
error | string | — | Error property for the DateRangePicker component | |
fixedWeeks | boolean | — | FixedWeeks property for the DateRangePicker component | |
helper | string | — | Helper property for the DateRangePicker component | |
inputVariant | outlinedfilledghostunderline | — | inputVariant property | |
isDateDisabled | (date: Date) => boolean | — | Predicate that disables specific dates. Throws are caught and logged; the date is then treated as allowed. | |
label | string | — | Label above the input. | |
locale | string | 'auto' | BCP 47 locale tag for date formatting — month names, weekday names and the
header title. Defaults to 'auto', which follows the active
<I18nProvider> locale, so an app that already declares its language does
not have to repeat it here. SSR-safe: the locale comes from context, so the
server and the client resolve the same tag (Intl with undefined would
follow the runtime and disagree across hydration). Falls back to the base
locale (en) when no provider is mounted. Pass an explicit tag
(e.g. 'de-DE', 'ja-JP') to override.
Until 2026-07-31 this defaulted to the literal 'de-DE', so an
English app rendered German month names unless every date component was
passed locale by hand. | |
maxDate | Date | — | MaxDate property for the DateRangePicker component | |
minDate | Date | — | MinDate property for the DateRangePicker component | |
mint | MintProp | 'none' | Micro-interaction preset forwarded to the inner Input. | |
name | string | — | Shared base name for native form submission. When set, two hidden
inputs are rendered — {name}_start and {name}_end — each
carrying the serialized date, so the visible input's locale-formatted
display string is never submitted instead.
Empty range submits both halves as "". The _start / _end
convention reflects the picker's domain language — date ranges read
naturally as start/end rather than min/max. | |
onClickOutside | () => void | — | Notification only — does NOT govern close behavior. | |
onEscape | () => void | — | Notification only — does NOT govern close behavior. | |
onOpenChange | (open: boolean) => void | — | onOpenChange property | |
onValueChange | (value: DateRange | undefined) => void | — | Fires when the selected range changes. During calendar selection
the user clicks twice — once to set the start, once to set the
end. onValueChange only fires when the range is *complete*
(start ≠ end); the intermediate { start: d, end: d } state
does NOT fire this callback. Use bind:value if you need the
in-progress state. | |
placeholder | string | — | Placeholder when no range is selected. | |
required | boolean | — | Required property for the DateRangePicker component | |
showOutsideDays | boolean | — | showOutsideDays property | |
showWeekNumbers | boolean | — | showWeekNumbers property | |
size | xssmmdlg +1 more | — | Size variant that controls dimensions and spacing of the DateRangePicker | |
value | DateRange | — | Currently selected date range. Supports bind:value. | |
valueFormat | dateiso | 'date' | Format used to serialise both range halves. See
DatePickerProps.valueFormat for semantics. | |
weekStartsOn | WeekdayIndex | — | weekStartsOn property | |
...HTMLAttributes<HTMLDivElement> inherited | HTMLAttributes | — | HTML attributes (excluding: 'children') |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
DatePickerProps | interface | props | 0 | — | |
DateRangePickerProps | interface | props | 0 | — | |
DatePickerPreset | interface | helper | 0 | — | |
DateRangePreset | interface | helper | 0 | — | |
MintProp | type | helper | 1 | — | |
MonthIndex | type | helper | 1 | Month index used by Date#getMonth(). | |
WeekdayIndex | type | helper | 1 | Weekday index used by Date#getDay(). | |
DateRange | interface | helper | 1 | A date range with inclusive start and end. | |
DateFormatOptions | type | helper | 1 | — | |
MintName | type | helper | 0 | A mint name: a built-in (autocompleted), 'none' to disable, or any
consumer-registered name. (string & {}) keeps the registry open — a
custom name still type-checks, it just isn't suggested. A typo therefore
also still compiles (it resolves like an unregistered custom name and
warns at runtime); the union buys completion and docs, not validation. | |
MintConfig | interface | helper | 0 | — | |
BuiltinMintName | type | helper | 0 | Built-in mint names as a literal union, so the mint prop autocompletes
across every component — the single list the hand-curated playground knobs
and docs used to drift away from. |
06 Installation
Import
import { DateRangePicker } from '@urbicon-ui/blocks';