Elements

Components / Forms

Calendar

A date-field calendar for selecting a single date or a range. Based on shadcn/ui Calendar — built on React DayPicker (Base UI has no calendar primitive) with Instant Strata styling. To attach it to a text field, see Date Input.

Installation

bash
npx shadcn add https://raw.githubusercontent.com/levi-putna/elements/main/registry/calendar/registry.json

Designing for date selection

  • Don't force the calendar. A calendar is great for browsing (“the second Tuesday”) but slow for known dates. Pair it with a typed field — see Date Input — so people can do either.
  • Show today and the selection clearly. Today is tinted; the selected day uses the primary colour; range ends are filled with the middle highlighted.
  • Help users reach distant dates. For birthdays or expiry dates, switch the caption to dropdowns so they can jump years without clicking the arrow dozens of times.
  • Constrain when you can. Disable impossible dates (past dates for a booking, weekends for a delivery) rather than letting users pick them and erroring later.
  • It's keyboard accessible. Arrow keys move day to day, Page Up/Down change months, and focus is managed for you.

Single date

The default mode="single" selects one date.

Single date
July 2026
tsx
import { Calendar } from "@/components/ui/calendar"

export function Example() {
  const [date, setDate] = React.useState<Date | undefined>(new Date())

  return <Calendar mode="single" selected={date} onSelect={setDate} />
}

Date range

Set mode="range" and render two months so the start and end are visible at once. Click a start date, then an end date; the span between them is highlighted.

Date range
July 2026
August 2026
tsx
import type { DateRange } from "react-day-picker"
import { Calendar } from "@/components/ui/calendar"

export function Example() {
  const [range, setRange] = React.useState<DateRange | undefined>()

  return (
    <Calendar
      mode="range"
      numberOfMonths={2}
      selected={range}
      onSelect={setRange}
    />
  )
}

Month & year dropdowns

For dates far from today — a date of birth, a passport expiry — captionLayout="dropdown" turns the caption into month and year selectors. Clamp the options with startMonth / endMonth.

Dropdown caption
July 2026
tsx
{/* Month + year dropdowns let users jump to a distant date fast. */}
<Calendar
  mode="single"
  captionLayout="dropdown"
  startMonth={new Date(1980, 0)}
  endMonth={new Date(2030, 11)}
  selected={date}
  onSelect={setDate}
/>

Disabled dates

Pass a matcher (or array of matchers) to disabled. This example blocks weekends and any date in the past — useful for a booking flow.

Disabled dates
July 2026
tsx
{/* Disable weekends and any past date. */}
<Calendar
  mode="single"
  selected={date}
  onSelect={setDate}
  disabled={[
    { dayOfWeek: [0, 6] },
    { before: new Date() },
  ]}
/>

Props

Calendar forwards all React DayPicker props. The most common ones:

PropTypeDefaultDescription
mode"single" | "multiple" | "range""single"Selection behaviour. Use range for start/end selection.
selectedDate | Date[] | DateRange-The currently selected date(s); shape depends on mode.
onSelect(value) => void-Fires when the selection changes.
numberOfMonthsnumber1How many months to render side by side — use 2 for ranges.
captionLayout"label" | "dropdown" | "dropdown-months" | "dropdown-years""label"Render the caption as plain text or as month/year dropdowns.
startMonth / endMonthDate-Clamp the navigable range (and the dropdown options).
disabledMatcher | Matcher[]-Dates that cannot be selected, e.g. { before: new Date() } or { dayOfWeek: [0, 6] }.
showOutsideDaysbooleantrueShow the leading/trailing days of adjacent months.
showWeekNumberbooleanfalseAdd an ISO week-number column.
buttonVariantButtonProps["variant"]"ghost"Variant used for the prev/next navigation buttons.