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
npx shadcn add https://raw.githubusercontent.com/levi-putna/elements/main/registry/calendar/registry.jsonDesigning 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.
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.
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.
{/* 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.
{/* 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:
| Prop | Type | Default | Description |
|---|---|---|---|
| mode | "single" | "multiple" | "range" | "single" | Selection behaviour. Use range for start/end selection. |
| selected | Date | Date[] | DateRange | - | The currently selected date(s); shape depends on mode. |
| onSelect | (value) => void | - | Fires when the selection changes. |
| numberOfMonths | number | 1 | How 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 / endMonth | Date | - | Clamp the navigable range (and the dropdown options). |
| disabled | Matcher | Matcher[] | - | Dates that cannot be selected, e.g. { before: new Date() } or { dayOfWeek: [0, 6] }. |
| showOutsideDays | boolean | true | Show the leading/trailing days of adjacent months. |
| showWeekNumber | boolean | false | Add an ISO week-number column. |
| buttonVariant | ButtonProps["variant"] | "ghost" | Variant used for the prev/next navigation buttons. |