Calendar

A bare calendar built on react-day-picker for selecting a single day, multiple days, or a date range, with keyboard navigation and dropdown month and year controls.

This is the primitive. If you want a typed date input — MM/dd/yyyy in English, dd/MM/yyyy in Vietnamese — a popover, and presets like “Today” or “Last 7 days” already wired up, use the Tier 2 composites DatePicker and DateRangePicker instead.

Basic usage

Use mode="single" for a one-day picker. The calendar manages its own selection state when left uncontrolled; leave mode off entirely and it renders read-only.

June 2026

Range selection

Set mode="range" and render two months with numberOfMonths for booking and reporting date pickers.

June 2026
July 2026

Multiple days

With mode="multiple" users can toggle any number of individual, non-contiguous days.

June 2026

Dropdown caption

Pass captionLayout="dropdown" to swap the static month label for month and year dropdowns — handy for jumping across distant dates like a birthdate. Bound the year list with startMonth / endMonth. The month names in the dropdown follow the same locale as the grid — see below.

June 2026

Localisation

Since 3.0.0 the calendar follows ComitorLocaleProvider, twice over. Weekday and month names come from the provider's date-fns locale — vi under locale="vi", enUS for every other code (give the provider dateFnsLocale={enGB} for British dates). The screen-reader labels react-day-picker attaches — the navigation buttons, the dropdowns, the name of every day — come from the provider's calendar strings. A date-fns locale carries no interface text, so a Vietnamese app used to hear react-day-picker's hard-coded English, “Go to the Previous Month”, in the middle of a Vietnamese screen. A locale prop still wins for the names, a labels prop for the labels.

The month dropdown is abbreviated with the code of whichever locale is in force, so a calendar with no locale no longer mixes an English grid with a vi-VN dropdown, as it did before 3.0.0. It still never asks for toLocaleString('default'), as the vendored copy did: that resolves against whatever locale the process is running under, so the server and the browser could produce different strings and React would report a hydration mismatch.

June 2026
Tháng Sáu 2026

Disabled dates

The disabled prop accepts a matcher — here every day before June 17 is non-selectable and dimmed.

June 2026

Content inside a day

CalendarDayButton is exported so you can add a second line to a day — an event count, a price, a status dot — without rebuilding the cell. Swap components.DayButton for a wrapper that renders the day number plus your own <span>; the component styles that span at text-xs and opacity-90.

June 2026

Props

PropTypeDefaultDescription
mode'single' | 'multiple' | 'range'—Selection behavior — a single day, an array of days, or a contiguous date range. Leave it off for a read-only calendar.
selectedDate | Date[] | DateRange—Controlled selected value. Pair with onSelect; omit for an uncontrolled calendar. The shape follows mode.
onSelect(value) => void—Called when the selection changes. Shape matches the active mode.
defaultMonthDatecurrent monthThe month displayed on first render when uncontrolled.
month / onMonthChangeDate / (month: Date) => void—Controls which month is displayed. Use both to drive navigation from outside the calendar.
startMonth / endMonthDate—Bounds for navigation, and the range the year dropdown offers under captionLayout="dropdown".
numberOfMonthsnumber1How many months to render side by side. Useful for range pickers.
captionLayout'label' | 'dropdown' | 'dropdown-months' | 'dropdown-years''label'Renders the month/year as a static label or as navigable dropdowns.
buttonVariant'default' | 'primary' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link''ghost'Button variant applied to the previous/next navigation buttons.
showOutsideDaysbooleantrueShow days from adjacent months that fill the leading and trailing weeks.
disabledMatcher | Matcher[]—Days that cannot be selected — a date, range, predicate, or matcher object.
localeLocale (date-fns)provider’sWeekday and month names, and the abbreviations in the month dropdown. Without it the calendar takes the date-fns locale of ComitorLocaleProvider: vi under locale="vi", enUS for every other code unless the provider is given a dateFnsLocale.
labelsPartial<Labels>provider’sThe screen-reader labels react-day-picker attaches — navigation buttons, dropdowns, the name of each day. Defaults to the calendar strings of ComitorLocaleProvider (“Go to previous month”, “Today, …, selected”). A locale from react-day-picker/locale carries labels of its own, which then win over the provider’s; this prop wins over both.
formattersPartial<Formatters>—Per-slot label formatters. Merged over the component’s own formatMonthDropdown, so overriding one leaves the rest intact.
componentsPartial<CustomComponents>—Swap individual internals. Merged over the component’s defaults — pass DayButton to build on CalendarDayButton rather than replacing it.
classNamestring—Additional Tailwind classes merged onto the calendar root.
classNamesPartial<ClassNames>—Override individual element class names (day, weekday, nav, etc.).

All other react-day-picker props are forwarded to the underlying DayPicker.

Accessibility

  • The grid follows the WAI-ARIA date picker pattern — days are reachable with arrow keys, and Enter or Space selects the focused day.
  • Previous and next navigation render as real <button> elements with accessible labels — “Go to previous month” and “Go to next month”, in the provider's language — and take aria-disabled at the ends of the navigable range. A disabled day instead gets the native disabled attribute, so it drops out of the tab order; style it from data-disabled on the cell.
  • The day cell carries aria-selected, and the state is spelled out in the day button's aria-label — today reads “Today, …” and a chosen day ends in “, selected” (“Hôm nay, …” and “, đã chọn” in Vietnamese) — so neither state rests on colour alone. react-day-picker emits no aria-current; today reaches CSS as data-today.
  • Focused days receive a visible focus-visible ring using the brand ring token, and focus is automatically moved to the focused day when navigating months.
  • A selected day is a control in its on state, so it uses the control pair — bg-control-on with text-control-check — the same pair as a checked Checkbox. Splitting the pair works by accident in the default palette, where the two tokens happen to be equal, and breaks in the high-contrast one.
  • Extra content you render inside a day is capped at opacity-90. The day stays clickable and focusable, so it is not exempt from the 4.5:1 text threshold. The worst case is a selected day in the light high-contrast palette: opacity-80 lands at 4.43:1 and fails, 85% clears at 4.79:1, and the shipped 90% leaves headroom at 5.15:1.
  • Known limitation: the today and range middle tints both use --accent, which sits near 1.1:1 against the calendar background. They read as a soft cue, not as information — pair them with a real signal if the state has to be perceivable on its own.