Calendar
StableA month grid for choosing a single date, several dates or a range — the building block of every date picker.
Anatomy
- 1CaptionThe month on display. With a dropdown caption, it becomes month and year selects.
- 2TodayMarked with a subtle fill, so it's easy to find without looking selected.
- 3NavigationPrevious and next month. They disable at the edges of the allowed range.
- 4Selected dayFilled with the brand color. Ranges also tint the days in between.
- 5WeekdayColumn headers, abbreviated, starting on the locale's first day of the week.
Installation
$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/calendar.jsonUsage
import { Calendar } from "@/components/ui/calendar"const [date, setDate] = React.useState<Date | undefined>(new Date())
<Calendar mode="single" selected={date} onSelect={setDate} />Calendar wraps React DayPicker with prfct styling. Selection uses the brand scale: selected days fill with brand-9, the span of a range with brand-3, and today is set in bold on a quiet fill.
today prop, so statically rendered pages match what the browser renders. In your app, omit it.Examples
Range
mode="range" selects a start and an end; numberOfMonths={2} shows both ends of longer ranges at once. Disable past days with disabled={{ before: today }} rather than validating after the fact.
Multiple dates
mode="multiple" toggles individual days. Cap the selection with max and show the count, so people know when they've reached it.
3 of 5 office days selected
Month and year dropdowns
For dates far from today — a birth date, an expiry year — captionLayout="dropdown" replaces the caption with month and year selects. Bound them with startMonth and endMonth.
Unavailable dates
disabled accepts dates, ranges, days of the week and functions. Combine it with modifiers to explain why a day is unavailable — here booked days are struck through, while weekends are simply dimmed.
Date picker
The everyday date field: a Button that shows the chosen date and opens the calendar in a Popover. Close the popover on selection and format the date for people, not machines.
The task becomes overdue after this day.
Localized
Pass a locale from react-day-picker/locale to translate month and day names and start weeks on the right day. Here, Polish — weeks start on Monday — with ISO week numbers.
Guidelines
When to use
- To pick dates near today, where seeing the week and month context helps: bookings, due dates, availability.
- To choose ranges, where seeing both ends together prevents mistakes.
When not to use
- For dates people know by heart and far from today, such as a birth date — three inputs or a text field with a clear format is faster than paging back decades.
- For approximate times such as next week — offer presets with a Select or buttons.
Make constraints visible
Disable days that can't be chosen instead of rejecting them after selection, and say why in the surrounding copy (Weekends are unavailable). Never let people build a selection that's only rejected on submit.
Formats
Show dates in words in the trigger (September 26, 2026) using Intl.DateTimeFormat with the person's locale, and store them in ISO 8601. Ambiguous numeric formats like 09/10 read differently across countries.
Accessibility
The calendar renders a grid of buttons with roving focus. Each day is announced with its full date, selection and disabled state; the caption is a live region that announces the month as people navigate.
| Key | Behavior |
|---|---|
←→ | Moves focus to the previous or next day. |
↑↓ | Moves focus to the same day in the previous or next week. |
PageUpPageDown | Moves to the same day in the previous or next month. |
ShiftPageUpShiftPageDown | Moves to the same day in the previous or next year. |
HomeEnd | Moves to the first or last day of the week. |
EnterSpace | Selects the focused day. |
- Focus. The focused day shows the standard 2px focus ring; the selected day is also marked with
aria-selected, never with color alone. - In a popover. The trigger button's text should state the current value (Pick a date or the formatted date), and focus returns to it when the popover closes.
- Contrast. Selected days use
brand-9with the brand's contrast color (≥ 4.5:1); outside and disabled days are dimmed but remain readable.
API reference
Calendar
Accepts every prop of DayPicker. The most used:
CalendarDayButton
The button rendered for each day. Replace it through components={{ DayButton }} to add content such as prices or availability dots.