Calendar
Date and date-range selection, with arrow or dropdown month navigation.
November 2026
SuMoTuWeThFrSa
No date selected
Range mode
onRangeChange fires while the range is still being built, so you'll receive { from } with no to after the first click. Don't treat that as a complete selection — wait for both ends before acting on it.
November 2026
SuMoTuWeThFrSa
Pick a start → pick an end
Dropdown navigation
Arrows are fine for dates near today. For a birth date or a far-future renewal, dropdowns save a lot of clicking.
SuMoTuWeThFrSa
Limiting the range
November 2026
SuMoTuWeThFrSa
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| mode | 'single' | 'range' | 'single' | Range mode uses range/onRangeChange instead of value/onChange. |
| value | Date | Selected date, single mode. | |
| onChange | (date: Date) => void | Fires on select, single mode. | |
| range | { from?: Date; to?: Date } | Selected range, range mode. Both ends are optional while the user is mid-selection. | |
| onRangeChange | (range: DateRange) => void | Fires as the range is built, so expect a from-only value partway through. | |
| headerType | 'arrows' | 'dropdowns' | 'arrows' | Month navigation style. |
| defaultMonth | Date | Month shown on first render. Useful when the selection is far from today. | |
| minDate | Date | Earliest selectable date. | |
| maxDate | Date | Latest selectable date. |
Why it works this way
Why
- Every day carries a full-date aria-label, so a grid of numbers is still understandable to a screen reader.
- In range mode, clicking before the start swaps the endpoints, so people can't build an invalid range.
Don't
- Don't use it when a typed date is faster. Pair it with an Input.