Calendar
Choose a date, several dates or a date range.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Installation
pnpm dlx shadcn@4.18.0 add @siteplane/calendarComplete the one-time setup first. Registry JSON lists source files and dependencies.
For this page’s examples, also install:
pnpm dlx shadcn@4.18.0 add @siteplane/buttonUsage
import { Calendar } from "@/siteplane/components/calendar";
<Calendar aria-label="Due date" defaultValue={new Date(2026, 9, 14)} />;Calendar takes native Date values, like the date pickers; only the day counts.
Date range
Set selectionMode="range" to select a start and an end date. The value is a { from, to } object. The start, the end and the days between them fade in like a single selected day, and change at once when the user prefers reduced motion.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
Shortcuts
Pass shortcuts to a range calendar for quick choices such as "Last week". They show as links in the accent color between the heading and the days. A choice selects its range and shows the month the range starts in. One that starts or ends on a date the calendar doesn't allow is disabled.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
Multiple dates
Set selectionMode="multiple" to select several separate dates. The value is an array of dates.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
30 | 31 | 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 1 | 2 | 3 |
Unavailable dates
Use startMonth and endMonth for the booking window and disabled for dates that are already taken. Unavailable dates can't be selected.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Today button
Control value and month when your own actions change the date, such as a Today button that selects today and shows its month. The example fixes its today with todayDate, like its dates; without todayDate, today is the device's date.
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Locale
Pass locale to format one calendar differently from the surrounding locale provider. Month names, weekday order and the first day of the week follow the locale.
| Mo | Di | Mi | Do | Fr | Sa | So |
|---|---|---|---|---|---|---|
28 | 29 | 30 | 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 1 |
Calendar options
CalendarOptions holds defaultMonth, disabled, startMonth, endMonth and todayDate, the options that Calendar shares with the calendarProps of Date Field, Date Picker, Date Range Picker and Date Time Picker. Define them once and pass the same object to a Calendar, or to a picker as calendarProps={booking}.
import {
Calendar,
type CalendarOptions,
} from "@/siteplane/components/calendar";
const booking: CalendarOptions = {
startMonth: new Date(2026, 9, 1),
endMonth: new Date(2027, 2, 1),
disabled: [{ before: new Date(2026, 9, 12) }, { dayOfWeek: [0, 6] }],
};
<Calendar aria-label="Booking date" {...booking} />;disabled takes a CalendarDayMatcher or a list of them. A matcher is a Date, a (date: Date) => boolean test, or a rule. In a rule, before and after match the days before or after a date, from and to a span of days with both ends included, and dayOfWeek the weekdays it lists, from 0 for Sunday to 6 for Saturday. A matched day shows struck out and can't be chosen. disabled={true} disables the whole calendar instead. startMonth and endMonth limit the months people can move to.
todayDate sets the day that counts as today, for an application whose today is not the device's, such as a demo fixed on one date or a schedule kept in another time zone. The calendar marks that day, and without a selected date it opens on its month. Without todayDate, today is the device's date.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
selection | single | multiple | range | single | Selects the calendar behavior and value shape. |
aria-label | string | unset | Accessible name of the calendar. |
size | sm | md | lg | xl | provider / lg | Calendar sizing. |
locale | string | locale context | Local language/calendar formatting override. |
value / default | Date | null, Date[] or { from, to } | uncontrolled | Selection shape follows selectionMode. |
on | (value: Date | Date[] | { from, to }) => void | unset | Reports the selection, in the shape of value. |
month / default | Date | selected date or today | The month the calendar shows, as any day in it. With two months, a month outside the ones on screen shows first. |
on | (month: Date) => void | unset | Reports the first day of the month that takes focus. |
disabled | boolean | matcher | matcher[] | false | true disables the calendar; matchers make the days they match unavailable. |
read | boolean | false | Keeps the current selection. |
start | Date | unset | The first and last month the calendar allows. |
today | Date | the device's date | The day marked as today; without a selected date, the calendar opens on its month. |
aria-invalid | AriaAttributes[aria-invalid] | unset | Marks the selected days invalid. |
number | number | 1 | Range mode only. Consecutive months shown side by side. |
shortcuts | { label, value: { from, to } }[] | unset | Range mode only. Quick choices shown under the heading. |
Matchers and the options shared with the date pickers are described in
Calendar options. The module exports CalendarProps with
CalendarSingleProps, CalendarMultipleProps and CalendarRangeProps for the
three selection modes, CalendarSelectionMode, CalendarOptions,
CalendarDayMatcher, DateRange for a { from, to } value and
CalendarRangeShortcut for a shortcut. The component renders its own
header, month and year picker and month grid; it accepts no custom children. It
shows one month, except in range mode with numberOfMonths={2}, which
renders two consecutive months side by side with one shared selection and one
Previous/Next pair. Each of the two months has its own heading with a month and
year picker: choosing a month there moves both months so that the chosen one
shows at that position, while Previous and Next page both months by one. With
two months the shortcuts show once, above both. A shortcut, or a month you
set, that leads to a month outside the two on screen shows that month first,
also when it lies before them; a month already on screen stays where it is.
Usage notes
A selected day fills with the brand accent, and its number takes the text color for accent fills, --sp-rw-fg-on-accent, which a light brand sets dark (Theming).
Arrow keys move between days, and the month heading opens a month and year picker. That heading button and the picker use English accessible labels in every locale. todayDate moves the today mark and the first month, but screen readers still hear the device's date named as today, because React Aria names that day. Use DatePicker or DateField for compact date entry. The Calendar reports days as native Dates at local midnight; persist the day deliberately, for example as YYYY-MM-DD.