Skip to content

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.

Layout & Display15 propsAccessibility notes

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

calendar.tsx
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

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

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

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

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

ariaLabel

string

default: month name

Accessible label for the grid.

Styling

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.