Calendar component
Calendar is a standalone month-grid view with single or range selection, min/max and per-day constraints, a `renderDay` slot for event markers, and full keyboard navigation of the grid.
Playground
Change a prop on the right and the preview updates. Switch to Code to copy what you see.
Recipes
Copy-paste patterns for the ways Calendar actually gets used — each one a real, self-contained example.
Single date
A standalone month grid with full keyboard navigation. value/onChange use ISO date strings (YYYY-MM-DD).
const [value, setValue] = useState<string | null>(null);
<Calendar value={value} onChange={setValue} />Date range
selectionMode="range" selects a start–end window; the value becomes a { start, end } pair of ISO strings.
const [range, setRange] = useState<CalendarRangeValue | null>(null);
<Calendar selectionMode="range" value={range} onChange={setRange} />Constrain selectable days
minValue/maxValue bound the range; isDateUnavailable disables specific days (weekends here) — both are skipped by keyboard navigation.
<Calendar
value={value}
onChange={setValue}
minValue={new Date()}
isDateUnavailable={(date) => date.getDay() === 0 || date.getDay() === 6}
/>Example
const [range, setRange] = useState<{ start?: Date; end?: Date }>({});
<Calendar
mode="range"
value={range}
onChange={setRange}
minDate={new Date()}
/>Props
The full public API for Calendar. Every prop is stable and intentionally typed.
Behavior & states
| Prop | Type | Default | Description |
|---|---|---|---|
| selectionMode | "single" | "range" | single | Single-date or start/end range selection. |
| onChange | (value) => void | — | Fires with null when a single selection is toggled off. |
| onMonthChange | (month: Date) => void | — | Fired when the visible month changes. |
| minValue / maxValue | string | Date | — | Days outside the bound are disabled. |
| isDateUnavailable | (date: Date) => boolean | — | Per-day disable predicate. |
| isDisabled | boolean | false | Disable the whole calendar. |
selectionMode
"single" | "range"
default: single
Single-date or start/end range selection.
onChange
(value) => void
default: —
Fires with null when a single selection is toggled off.
onMonthChange
(month: Date) => void
default: —
Fired when the visible month changes.
minValue / maxValue
string | Date
default: —
Days outside the bound are disabled.
isDateUnavailable
(date: Date) => boolean
default: —
Per-day disable predicate.
isDisabled
boolean
default: false
Disable the whole calendar.
Data
| Prop | Type | Default | Description |
|---|---|---|---|
| value / defaultValue | string | null · { start, end } | null | — | Controlled / initial selection. Dates are YYYY-MM-DD. |
| month / defaultMonth | string | Date | selection, else today | Displayed month. Accepts "YYYY-MM", "YYYY-MM-DD", or a Date. |
value / defaultValue
string | null · { start, end } | null
default: —
Controlled / initial selection. Dates are YYYY-MM-DD.
month / defaultMonth
string | Date
default: selection, else today
Displayed month. Accepts "YYYY-MM", "YYYY-MM-DD", or a Date.
Layout & positioning
| Prop | Type | Default | Description |
|---|---|---|---|
| weekStartsOn | 0 | 1 | 0 | Sunday or Monday. |
| showOutsideDays | boolean | true | Render leading/trailing days of adjacent months. |
| fixedWeeks | boolean | false | Always 6 rows so height doesn't jump between months. |
weekStartsOn
0 | 1
default: 0
Sunday or Monday.
showOutsideDays
boolean
default: true
Render leading/trailing days of adjacent months.
fixedWeeks
boolean
default: false
Always 6 rows so height doesn't jump between months.
Appearance
| Prop | Type | Default | Description |
|---|---|---|---|
| renderDay | (ctx) => ReactNode | day number | Custom day content. ctx: { date, inMonth, isToday, isSelected, isRangeStart, isRangeEnd, inRange, isDisabled }. |
| size | "sm" | "md" | "lg" | md | Cell size. |
renderDay
(ctx) => ReactNode
default: day number
Custom day content. ctx: { date, inMonth, isToday, isSelected, isRangeStart, isRangeEnd, inRange, isDisabled }.
size
"sm" | "md" | "lg"
default: md
Cell size.
Accessibility
| Prop | Type | Default | Description |
|---|---|---|---|
| ariaLabel | string | month name | Accessible label for the grid. |
ariaLabel
string
default: month name
Accessible label for the grid.
Styling
| Prop | Type | Default | Description |
|---|---|---|---|
| classNames | { base?, header?, heading?, nav?, navButton?, grid?, weekday?, week?, day? } | — | Slot classes. |
classNames
{ base?, header?, heading?, nav?, navButton?, grid?, weekday?, week?, day? }
default: —
Slot classes.
Accessibility
- The grid is a `role="grid"` with arrow-key navigation, Home/End for week edges, and PageUp/PageDown to change month.
- The focused day is tracked with a roving tabindex; only one day is in the tab order at a time.
- Disabled days are `aria-disabled` and skipped by keyboard navigation.