Components Forms
DateRangePicker
StablePicks a start and end date from a two-month calendar, with optional preset ranges.
Use DateRangePicker for reporting periods, campaign windows and filters. The popover closes once both dates are chosen. Presets cover common ranges in one click. With startName and endName, the dates are submitted as ISO values.
Overview
import { DateRangePicker, FormField } from '@bindshq/stance-react';
const TODAY = new Date(2026, 10, 11);
export function Basic() {
return (
<FormField label="Campaign window" style={{ width: 300 }}>
<DateRangePicker locale="en-GB" today={TODAY} startName="from" endName="to" />
</FormField>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { DateRangePicker } from '@bindshq/stance-react';Platforms
DateRangePicker has native counterparts. They share Stance tokens and intent, and use each platform’s own controls and conventions. The live previews on this page are the web implementation.
import { DateRangePicker } from '@bindshq/stance-react';Examples
import { DateRangePicker, FormField } from '@bindshq/stance-react';
const TODAY = new Date(2026, 10, 11);
const day = (d: Date, n: number) => new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
const presets = [
{ label: 'Today', range: (t: Date) => ({ start: t, end: t }) },
{ label: 'Last 7 days', range: (t: Date) => ({ start: day(t, -6), end: t }) },
{ label: 'Last 30 days', range: (t: Date) => ({ start: day(t, -29), end: t }) },
{
label: 'This month',
range: (t: Date) => ({
start: new Date(t.getFullYear(), t.getMonth(), 1),
end: new Date(t.getFullYear(), t.getMonth() + 1, 0),
}),
},
];
export function WithPresets() {
return (
<FormField label="Reporting period" style={{ width: 300 }}>
<DateRangePicker
locale="en-GB"
today={TODAY}
presets={presets}
defaultValue={presets[1]!.range(TODAY)}
/>
</FormField>
);
}No range chosen
import { useState } from 'react';
import { DateRangePicker } from '@bindshq/stance-react';
import type { DateRange } from '@bindshq/stance-react';
const TODAY = new Date(2026, 10, 11);
export function Controlled() {
const [range, setRange] = useState<DateRange>({ start: null, end: null });
return (
<div style={{ display: 'grid', gap: 8, width: 300 }}>
<DateRangePicker
aria-label="Deployment window"
locale="en-GB"
today={TODAY}
min={TODAY}
value={range}
onValueChange={setRange}
/>
<p style={{ margin: 0, fontSize: 13 }}>
{range.start && range.end
? `${range.start.toDateString()} → ${range.end.toDateString()}`
: 'No range chosen'}
</p>
</div>
);
}API reference
| Prop | Type | Default | Description |
|---|---|---|---|
value / defaultValue | { start: Date | null; end: Date | null } | None | Selected range. |
onValueChange | (value: DateRange) => void | None | Called as the range changes. |
presets | { label: string; range: (today: Date) => DateRange }[] | None | Shortcut ranges beside the calendar. |
startName / endName | string | None | Hidden input names for form submission. |
min / max / isDateDisabled | Date | null / (date) => boolean | None | Availability rules, as in Calendar. |
numberOfMonths | 1 | 2 | 2 | Months shown. |
locale / weekStartsOn | string / 0 to 6 | None | Localisation. |
size | 'sm' | 'md' | 'lg' | 'md' | Trigger height. |
invalid | boolean | None | Invalid styling and aria-invalid. |
labels | Partial<DateRangePickerLabels> | None | placeholder, calendar, presets, clear. |
Accessibility
- The trigger is a button named by its FormField label, showing the current range or placeholder.
- The popover is a labelled dialog; presets are a named group of toggle buttons with aria-pressed.
- Range start and end days are announced as such, and in-range days are marked selected.
- Focus returns to the trigger when the popover closes.
Keyboard
| Keys | Action |
|---|---|
| Enter Space | Open the picker / choose a day or preset. |
| Arrows Page Up/Down | Navigate days (see Calendar). |
| Esc | Close without changing the range. |
Usage guidelines
Do
- Offer presets for analytics views.
- Show the chosen range in the page heading or summary.
Don’t
- Use two separate DatePickers for one range.