Calendar
Chassis Calendar is a standalone date grid — the same calendar DatePicker renders in its popover, usable on its own.
Overview
Calendar is a date grid with keyboard-navigable cells, usable on its own wherever an inline calendar is needed — no popover, no text field. DatePicker renders this same component inside its popover, so the two always look and behave identically.
import { Calendar } from '@chassis-ui/react'
export const Example = () => {
return <Calendar aria-label="Event date" />
}Min & max dates
Restrict the selectable range with minValue/maxValue. Dates outside the range are shown but disabled.
import { Calendar } from '@chassis-ui/react'
import { today, getLocalTimeZone } from '@internationalized/date'
export const Example = () => {
const now = today(getLocalTimeZone())
return <Calendar aria-label="Appointment date" maxValue={now.add({ days: 30 })} minValue={now} />
}Unavailable dates
Pass isDateUnavailable to mark specific dates as unselectable without excluding them from the grid — useful for booked dates, holidays, or (as below, using @internationalized/date's locale-aware isWeekend) weekends. Unavailable dates remain focusable so screen reader users can discover why they can't be picked.
import { Calendar } from '@chassis-ui/react'
import { isWeekend } from '@internationalized/date'
export const Example = () => {
return (
<Calendar
aria-label="Appointment date"
isDateUnavailable={(date) => isWeekend(date, 'en-US')}
/>
)
}From a list of dates
For data-driven cases — booked dates fetched from an API, say — pass unavailableDates instead: a plain array of ISO 8601 (YYYY-MM-DD) strings. It composes with isDateUnavailable when both are given; a date unavailable by either is unavailable.
import { Calendar } from '@chassis-ui/react'
import { today, getLocalTimeZone } from '@internationalized/date'
export const Example = () => {
const now = today(getLocalTimeZone())
const unavailableDates = [now.add({ days: 2 }), now.add({ days: 5 }), now.add({ days: 9 })].map(
(date) => date.toString()
)
return <Calendar aria-label="Appointment date" unavailableDates={unavailableDates} />
}Weekend styling
Saturday/Sunday (or whichever days the active locale considers the weekend — determined via @internationalized/date's isWeekend) are shown muted automatically, no prop required. This is purely visual — weekends stay selectable unless also marked unavailable. Every example on this page shows it. See Internationalization for how the active locale is set.
Navigation
Calendar shows month and year <select>s next to the prev/next buttons, for jumping further than one page at a time.
First day of week
The week starts on Monday by default, regardless of the active locale. Pass firstDayOfWeek to override it — 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'.
import { Calendar } from '@chassis-ui/react'
export const Example = () => {
return <Calendar aria-label="Event date" firstDayOfWeek="sun" />
}Multiple dates
Pass selectionMode="multiple" to select any number of independent dates instead of one — clicking a date toggles it on or off, and value/defaultValue/onChange switch from a single date to an array of dates. Unlike RangeCalendar, the selected dates don't need to be contiguous.
import { Calendar } from '@chassis-ui/react'
import { today, getLocalTimeZone } from '@internationalized/date'
export const Example = () => {
const now = today(getLocalTimeZone())
return (
<Calendar
aria-label="Event dates"
defaultValue={[now, now.add({ days: 5 }), now.add({ days: 12 })]}
selectionMode="multiple"
/>
)
}Multiple months
Pass visibleMonths to show more than one month side by side, sharing one selection — independent of selectionMode, so it works the same with a single selected date or several. Each month gets its own header with its own month/year dropdowns, kept in sync. The prev/next buttons move to a single global overlay instead of repeating in every month's header, paging the whole visible span at once.
import { Calendar } from '@chassis-ui/react'
import { today, getLocalTimeZone } from '@internationalized/date'
export const Example = () => {
const now = today(getLocalTimeZone())
return <Calendar aria-label="Event date" defaultValue={now} visibleMonths={2} />
}Styling
Like DatePicker, Calendar isn't styled by @chassis-ui/css — chassis-css's own datepicker styling is built entirely around Vanilla Calendar Pro's specific DOM output, which React Aria's calendar hooks don't produce. Calendar instead ships its own scoped CSS (built on @chassis-ui/tokens' public design tokens, so it still matches the current theme and light/dark mode) in @chassis-ui/react/style.css, which an app imports once (see Theming).
Accessibility
Calendar has no visible label of its own — pass aria-label or aria-labelledby any time it's used standalone (as in every example above). When composed inside DatePicker's popover, the accessible name instead comes from the picker's own label.
Scope
- Contiguous ranges —
Calendarselects independent dates or none at all; for a start/end range, seeRangeCalendar.
API
Calendar
| Prop | Type | Default |
|---|---|---|
aria-label? | string | — |
| An accessible label for the calendar, used when there's no visible label. Required for standalone use — a calendar grid has no other accessible name of its own. | ||
aria-labelledby? | string | — |
| Identifies a visible label element for the calendar. | ||
autoFocus? | boolean | — |
| Whether to automatically focus the calendar when it mounts. | ||
className? | string | — |
| A string of all className you want applied to the base component. | ||
defaultFocusedValue? | DateValue | null | — |
The date the calendar first shows and focuses, in place of the selected date or today.
DatePicker and DateRangePicker pass their placeholderValue here while empty. | ||
defaultValue? | DateValue | DateValue[] | null | — |
| The initial selected date (uncontrolled). The initial selected dates (uncontrolled). | ||
disabled? | boolean | — |
| Prevents the calendar from being focused or interacted with. | ||
firstDayOfWeek? | "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | 'mon' |
| The day that starts the week, overriding the default set by the active locale. | ||
isDateUnavailable? | ((date: DateValue) => boolean) | — |
Callback that is called for each date in the calendar. If it returns true, that date is
shown but cannot be selected. | ||
labels? | Partial<CalendarLabels> | — |
Overrides for the handful of strings this component renders itself rather than getting from
the active locale (the year view's paging arrows, its live-region announcements, and the
month/year header buttons). Everything else — month and weekday names, the calendar system —
follows I18nProvider's locale via react-aria and needs no override. Merged over the English
defaults, so passing one key leaves the rest alone. | ||
maxValue? | DateValue | null | — |
| The maximum allowed date that a user may select. | ||
minValue? | DateValue | null | — |
| The minimum allowed date that a user may select. | ||
onChange? | ((value: DateValue) => void) | ((value: DateValue[]) => void) | — |
Callback fired when the selected date changes. Unlike DatePicker's onChange (whose
segmented field can be cleared to null), a calendar selection is always a concrete date.
Callback fired when the set of selected dates changes. | ||
selectionMode? | "single" | "multiple" | 'single'
'single' |
| Whether a single date or multiple, independently toggled dates can be selected. | ||
unavailableDates? | string[] | — |
ISO 8601 dates (YYYY-MM-DD) to mark unselectable, as a convenience alternative to
isDateUnavailable for data-driven cases (e.g. booked dates fetched from an API). Composed
with isDateUnavailable when both are given — a date unavailable by either is unavailable. | ||
value? | DateValue | DateValue[] | null | — |
| The selected date (controlled). The selected dates (controlled). | ||
visibleMonths? | number | 1 |
| Number of months to display side by side, sharing one selection. Wraps to multiple rows in a narrow container (e.g. a popover on a sm screen) rather than overflowing — the wrap is driven by the calendar's own width, not the viewport, so it adapts correctly regardless of where the calendar is embedded. | ||