Skip to content

Calendar

Stable

A month grid for choosing a single date, several dates or a range — the building block of every date picker.

September 2026

Anatomy

September 2026
  1. 1CaptionThe month on display. With a dropdown caption, it becomes month and year selects.
  2. 2TodayMarked with a subtle fill, so it's easy to find without looking selected.
  3. 3NavigationPrevious and next month. They disable at the edges of the allowed range.
  4. 4Selected dayFilled with the brand color. Ranges also tint the days in between.
  5. 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.json

Usage

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.

Dates in these examples
Examples pin “today” to a fixed date with the 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.

September 2026
October 2026

Multiple dates

mode="multiple" toggles individual days. Cap the selection with max and show the count, so people know when they've reached it.

September 2026

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.

May 1990

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.

September 2026

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.

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.

wrzesień 2026
36
37
38
39
40

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.

Pick a delivery dayWeekdays only · next available: Thu 24
Do.Unavailable days are disabled up front; the rule is stated nearby.
Pick a delivery daySundays are not available. Please try again.
Don’t.Letting people choose a Sunday, then showing an error 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.

KeyBehavior
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-9 with 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:

PropTypeDefault
mode

What can be selected.

"single" | "multiple" | "range"No default
selected

The selection. Use with onSelect.

Date | Date[] | DateRangeNo default
onSelect

Called when the selection changes.

(selected, day, modifiers, event) => voidNo default
defaultMonth

The month shown first.

DateNo default
month / onMonthChange

Controls the displayed month.

Date / (month) => voidNo default
numberOfMonths

How many months to show side by side.

number1
captionLayout

A text caption, or dropdowns for months and/or years.

"label" | "dropdown" | "dropdown-months" | "dropdown-years""label"
startMonth / endMonth

Bounds for navigation and dropdowns.

DateNo default
disabled

Days that can't be selected: dates, ranges, { before }, { dayOfWeek }, functions.

Matcher | Matcher[]No default
modifiers / modifiersClassNames

Custom day states and their classes.

Record<string, Matcher> / Record<string, string>No default
min / max

Selection limits in multiple and range modes.

numberNo default
locale

Translations and week start, e.g. pl from react-day-picker/locale.

LocaleNo default
weekStartsOn

Overrides the locale's first day of the week.

0 – 6No default
showWeekNumber

Adds a column of ISO week numbers.

booleanfalse
showOutsideDays

Shows days of adjacent months, dimmed. prfct turns it off by default when numberOfMonths is greater than 1, so days and selections aren't repeated.

booleannumberOfMonths <= 1
today

Overrides today's date.

DateNo default
buttonVariant

prfct-specific: the Button variant of the navigation arrows.

ButtonProps["variant"]"ghost"

CalendarDayButton

The button rendered for each day. Replace it through components={{ DayButton }} to add content such as prices or availability dots.