Calendar
A date field component that allows users to enter and edit date.
When to Use
Use Calendar when you need to:
- Let users pick a single date from a visual month grid
- Select a date range such as check-in/check-out or start/end dates
- Allow multiple individual date selections (e.g. availability)
- Provide inline date selection as part of a larger form layout
- Display a persistent calendar alongside other content
When Not to Use
- Date input triggered by a button or field - use Date Picker which wraps Calendar in a Popover
- Time-only selection - use a dedicated time picker component
- Quick relative dates (today, last 7 days) - use preset buttons instead
- Free-form date entry with keyboard - use a standard date input field
Default
Single date selection with dropdown month/year navigation.
Range Selection
Select a date range across two side-by-side months. The selected range is visually highlighted between start and end dates.
Multiple Selection
Select multiple individual dates. Each click toggles a date on or off.
Disabled Dates
Disable past dates or specific date matchers to restrict selection to valid options.
UX & Design Guidelines
Selection Modes
Use mode="single" for simple date fields (appointments, birthdays). Use mode="range" with numberOfMonths={2} for booking flows so users see start and end in context. Use mode="multiple" sparingly - only when users genuinely need to pick several discrete dates.
Navigation & Layout
Use captionLayout="dropdown" when users may need to jump to distant months or years (e.g. date of birth). Use the default captionLayout="label" when navigation is typically within a few months of today. For range selection, display numberOfMonths={2} on desktop and fall back to a single month on mobile.
Responsive Behavior
The calendar grid uses --cell-size (defaults to 32px) for day cells. On mobile, ensure the calendar fits within the viewport width. Multi-month layouts stack vertically on narrow screens via flex-col md:flex-row. When placed inside a Popover, the calendar auto-positions to avoid overflow.
Timezone Handling
If users report off-by-one date selection (selecting the 20th highlights the 19th), set the timeZone prop to the user's local timezone using Intl.DateTimeFormat().resolvedOptions().timeZone. This ensures the calendar interprets midnight boundaries correctly across timezones.
Accessibility
Keyboard Navigation
- Arrow Left / Arrow Right — Move focus to the previous/next day
- Arrow Up / Arrow Down — Move focus to the same day in the previous/next week
- Page Up / Page Down — Navigate to the previous/next month
- Home / End — Jump to the start/end of the current week
- Enter or Space — Select the focused date
Screen Reader Support
- Built on React DayPicker which provides full ARIA grid semantics
- Each day cell is announced with its full date and selection state
- Month/year navigation announces the new visible month
- Disabled dates are announced as unavailable via
aria-disabled - Use
aria-labelon the calendar to provide context (e.g. "Select departure date")
Focus Management
Visible focus ring with 3px width using ring-ring/50 and ring-[3px]. Focus is automatically moved to the selected or focused day cell. When navigating months, focus transfers to the same day number in the new month (or the last available day if the month is shorter).
Touch Targets
Day cells are sized via --cell-size (default 32px) with aspect-square to maintain consistent hit areas. Navigation arrows use the same cell size for their touch target. For touch-heavy interfaces, increase the cell size via CSS custom property overrides.
ARIA Attributes
{/* Calendar with accessible label */}
<Calendar
mode="single"
selected={date}
onSelect={setDate}
aria-label="Select a date"
/>
{/* Disabled dates with screen reader context */}
<Calendar
mode="single"
selected={date}
onSelect={setDate}
disabled={(d) => d < new Date()}
aria-describedby="date-constraint"
/>
<span id="date-constraint" className="sr-only">
Only future dates can be selected
</span>
{/* Range calendar with descriptive label */}
<Calendar
mode="range"
selected={dateRange}
onSelect={setDateRange}
numberOfMonths={2}
aria-label="Select check-in and check-out dates"
/>Related Components
Date Picker
Wraps Calendar in a Popover for button-triggered date selection with range and presets.
Popover
Floating container used to present the Calendar in dropdown-style date pickers.
Button
Used as the trigger element for Date Picker and for the calendar navigation arrows.
Input
Combine with Calendar for free-form date entry alongside visual date selection.
API Reference
The Calendar component accepts the following props in addition to standard React DayPicker attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
| mode | "single" | "multiple" | "range" | "single" | The selection mode of the calendar. |
| selected | Date | Date[] | DateRange | undefined | — | The selected date(s) based on mode. |
| onSelect | (date: ...) => void | — | Event handler called when a date is selected. |
| defaultMonth | Date | — | The month to display initially. |
| numberOfMonths | number | 1 | Number of months to display side by side. |
| captionLayout | "label" | "dropdown" | "dropdown-months" | "dropdown-years" | "label" | Layout of the month/year caption navigation. |
| disabled | Matcher | Matcher[] | — | Dates that should be disabled and non-selectable. |
| showOutsideDays | boolean | true | Show days from previous/next months in the grid. |
| showWeekNumber | boolean | false | Show ISO week numbers in the first column. |
| timeZone | string | — | IANA timezone for date display and selection (e.g. 'America/New_York'). |
| buttonVariant | "default" | "outline" | "ghost" | "destructive" | "secondary" | "link" | "ghost" | The variant applied to the previous/next navigation buttons. |
| className | string | — | Additional CSS classes for styling the calendar container. |