DateRangePicker
Start/end date input with a paired calendar popover, quick-range presets, and locale-aware segments.
Start and end segments are independent spinbuttons; popover dialog wraps a labelled range calendar with full keyboard control.
Rendered as a segmented control — the calendar icon, the start/end date segments, and the chevron sit as raised tiles inside a tinted track, matching the other inputs. The focus ring lives on the track.
import { DateRangePicker } from '@arshad-shah/cynosure-react';
import { parseDate } from '@internationalized/date';
export default function Example() {
return (
<div style={{ width: 380 }}>
<DateRangePicker
label="Stay"
defaultValue={{
start: parseDate('2026-04-17'),
end: parseDate('2026-04-22'),
}}
/>
</div>
);
}
Variants
Section titled “Variants”outline (default), filled, and ghost tint the segmented track — the same vocabulary as the other form controls.
import { DateRangePicker, Stack } from '@arshad-shah/cynosure-react';
import { parseDate } from '@internationalized/date';
export default function Example() {
const range = {
start: parseDate('2026-04-17'),
end: parseDate('2026-04-22'),
};
return (
<Stack gap="3" style={{ width: 380 }}>
<DateRangePicker label="Outline" variant="outline" defaultValue={range} />
<DateRangePicker label="Filled" variant="filled" defaultValue={range} />
<DateRangePicker label="Ghost" variant="ghost" defaultValue={range} />
</Stack>
);
}
sm, md (default), and lg.
import { DateRangePicker, Stack } from '@arshad-shah/cynosure-react';
import { parseDate } from '@internationalized/date';
export default function Example() {
const range = {
start: parseDate('2026-04-17'),
end: parseDate('2026-04-22'),
};
return (
<Stack gap="3" style={{ width: 380 }}>
<DateRangePicker label="Small" size="sm" defaultValue={range} />
<DateRangePicker label="Medium" size="md" defaultValue={range} />
<DateRangePicker label="Large" size="lg" defaultValue={range} />
</Stack>
);
}
States
Section titled “States”Disabled, read-only, invalid, and empty.
import { DateRangePicker, Stack } from '@arshad-shah/cynosure-react';
import { parseDate } from '@internationalized/date';
export default function Example() {
const range = {
start: parseDate('2026-04-17'),
end: parseDate('2026-04-22'),
};
return (
<Stack gap="3" style={{ width: 380 }}>
<DateRangePicker label="Default" defaultValue={range} />
<DateRangePicker label="Disabled" isDisabled defaultValue={range} />
<DateRangePicker label="Read only" isReadOnly defaultValue={range} />
<DateRangePicker label="Invalid" invalid defaultValue={range} />
<DateRangePicker label="Empty" aria-label="Empty range" />
</Stack>
);
}
Bounded range
Section titled “Bounded range”Use minValue / maxValue to bound either end of the range.
import { DateRangePicker } from '@arshad-shah/cynosure-react';
import { getLocalTimeZone, today } from '@internationalized/date';
export default function Example() {
const TODAY = today(getLocalTimeZone());
return (
<div style={{ width: 380 }}>
<DateRangePicker
label="Future bookings only"
minValue={TODAY}
defaultValue={{
start: TODAY.add({ days: 1 }),
end: TODAY.add({ days: 5 }),
}}
/>
</div>
);
}
Presets
Section titled “Presets”presets renders a left rail of DateRangePreset entries inside the popover. Clicking a preset commits the range and closes the popover.
import { DateRangePicker } from '@arshad-shah/cynosure-react';
import { getLocalTimeZone, today } from '@internationalized/date';
export default function Example() {
const TODAY = today(getLocalTimeZone());
return (
<div style={{ width: 420 }}>
<DateRangePicker
label="Report range"
presets={[
{
label: 'Last 7 days',
value: { start: TODAY.subtract({ days: 6 }), end: TODAY },
},
{
label: 'Last 30 days',
value: { start: TODAY.subtract({ days: 29 }), end: TODAY },
},
{
label: 'This month',
value: {
start: TODAY.set({ day: 1 }),
end: TODAY,
},
},
]}
/>
</div>
);
}
Visible months
Section titled “Visible months”Show one or two months side-by-side in the popover with visibleMonths. The popover collapses to one month below ~640px regardless.
import { DateRangePicker, Stack } from '@arshad-shah/cynosure-react';
import { parseDate } from '@internationalized/date';
export default function Example() {
const range = {
start: parseDate('2026-04-17'),
end: parseDate('2026-04-22'),
};
return (
<Stack gap="3" style={{ width: 380 }}>
<DateRangePicker label="One month" visibleMonths={1} defaultValue={range} />
<DateRangePicker label="Two months" visibleMonths={2} defaultValue={range} />
</Stack>
);
}
Accessibility
Section titled “Accessibility”- Start and end date segments are independent labelled
spinbuttons — arrow keys nudge each segment, Tab moves between them. - The popover is a dialog with a labelled range calendar; arrow keys traverse days, Enter selects.
invalidtogglesaria-invalidon the input group; range presets carry adata-activeflag for visual styling without altering semantics.- Pass
aria-label(or a stringlabel) so the field is announced even when no value is set.
Recipes
Section titled “Recipes”- Pass
presets={[{ label, value: { start, end } }]}for quick-range shortcuts like “Last 7 days”. - For narrow surfaces, set
visibleMonths={1}so the calendar fits in tight popovers. - Wrap in
LocaleProviderto flip segment order and first-day-of-week. - Compose with
FormField+FormMessageto surface “pick a range” validation.