Components
Calendar
An enhanced Calendar built on shadcn's Calendar primitive (react-day-picker). Clicking the month or year label in the caption switches to a button-grid panel for quick navigation — no native <select> dropdowns. Supports all DayPicker modes (single, range, multiple) and forwards DayPicker props, except the few the panel navigation reserves (see Notes).
Days view — click month or year in the caption
Months view (defaultView="months")
Years view (defaultView="years")
Multiple — click multiple days to toggle selection
Range — pick a start and end day
Installation
With the @easy-shadcn namespace configured:
pnpm dlx shadcn@latest add @easy-shadcn/calendarOr install via the full URL (zero configuration):
pnpm dlx shadcn@latest add https://easy-shadcn.vercel.app/r/calendar.jsonThe underlying shadcn calendar and button primitives (plus react-day-picker and date-fns) are installed automatically.
Usage
import { useState } from "react"
import { Calendar } from "@/components/easy/calendar"
const [date, setDate] = useState<Date>()
<Calendar mode="single" selected={date} onSelect={setDate} />Props
All props from shadcn's Calendar (i.e. react-day-picker's DayPicker) are forwarded. The following are the most common:
| Prop | Type | Default | Description |
|---|---|---|---|
defaultView | "days" | "months" | "years" | "days" | Initial view. Use "months" or "years" to start on that panel. |
mode | "single" | "range" | "multiple" | - | Selection mode. Controls selected / onSelect types. |
selected | Date | DateRange | Date[] | - | The selected value. Type narrows from mode. |
onSelect | (value) => void | - | Called when the selection changes. |
locale | Locale | - | date-fns locale for month / weekday labels and format. |
labels | Partial<Labels> | DayPicker defaults | Accessible labels, including the custom month and year controls. |
formatters | Partial<Formatters> | DayPicker defaults | Formats day, month, and year output; formatCaption is reserved. |
numerals | Numerals | "latn" | Numbering system used by the custom year surfaces and DayPicker. |
timeZone | string | local time zone | Time zone used when formatting custom month and year surfaces. |
numberOfMonths | number | 1 | Number of months shown side-by-side. |
disableNavigation | boolean | false | Disables primitive and custom month / year navigation. |
reverseMonths | boolean | false | Reverses visual month order without changing chronological navigation. |
reverseYears | boolean | false | Reverses the custom year-panel option order. |
defaultMonth | Date | current month | Initial month displayed. |
month | Date | - | Controlled visible month. Use with onMonthChange. |
onMonthChange | (month: Date) => void | - | Called when the visible month changes. |
startMonth | Date | today − 100 years | Earliest navigable month. Bounds the year panel. |
endMonth | Date | today + 100 years | Latest navigable month. |
disabled | Matcher | Matcher[] | - | Days matching the condition cannot be selected. |
monthsClassName | ClassValue | - | className for the months grid panel. |
yearsClassName | ClassValue | - | className for the years grid panel. |
resetViewsKey | number | - | Resets open panels to days when the token changes after mount. |
Views
The calendar manages three internal views:
- Days (default) — the standard day grid. The caption shows the month name and year as clickable buttons.
- Months — a 3×4 button grid (Jan – Dec). Click a month to jump to it and return to the day view. Click the year label at the top to switch to the years view.
- Years — a 3×4 button grid showing 12 years per page, with left/right arrows to paginate. Click a year to jump to it and enter the months view.
Notes
- Reserved props.
captionLayout,components,data-slot,dateLib,fixedWeeks, andhideNavigationare managed internally to drive the panel navigation, root marker, and date math.formatters.formatCaptionis reserved because the custom caption renders its own controls. These paths are rejected by TypeScript and stripped at runtime. If you need to customize them, use thecomponents/ui/calendarprimitive directly. labels.labelMonthDropdownandlabels.labelYearDropdownname both the caption triggers and their panels.formatMonthDropdown/formatYearDropdownalso apply to those custom surfaces and receive a locale-, numeral-, and time-zone-awareDateLib.classNames.month_caption,styles.month_caption, and DayPicker caption animation data are preserved on the custom caption.month_gridcustomization still belongs to the days grid; usemonthsClassName/yearsClassNamefor the two custom panels.- The month and year caption buttons replace the native
<select>dropdowns thatreact-day-pickerrenders withcaptionLayout="dropdown". This gives a consistent, styled experience across platforms. - The year panel defaults to ±100 years from today when
startMonth/endMonthare not provided. startMonth/endMonthalso bound the custom month panel, so out-of-range months are disabled before the day grid is shown.- Custom navigation follows DayPicker's
disableNavigation, controlledmonth,timeZone, multi-month order, actual visible slot count, and month-level bounds. Partial first and last decades remain reachable when they contain a valid year. resetViewsKeyis edge-triggered: its initial value preservesdefaultView; any later value change, including a change to0, resets every visible slot to days.- When
numberOfMonths > 1, clicking any month's caption enters the panel view. After selecting, all months update together.