Easy Shadcn
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")
2020 2029
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/calendar

Or install via the full URL (zero configuration):

pnpm dlx shadcn@latest add https://easy-shadcn.vercel.app/r/calendar.json

The 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:

PropTypeDefaultDescription
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.
selectedDate | DateRange | Date[]-The selected value. Type narrows from mode.
onSelect(value) => void-Called when the selection changes.
localeLocale-date-fns locale for month / weekday labels and format.
labelsPartial<Labels>DayPicker defaultsAccessible labels, including the custom month and year controls.
formattersPartial<Formatters>DayPicker defaultsFormats day, month, and year output; formatCaption is reserved.
numeralsNumerals"latn"Numbering system used by the custom year surfaces and DayPicker.
timeZonestringlocal time zoneTime zone used when formatting custom month and year surfaces.
numberOfMonthsnumber1Number of months shown side-by-side.
disableNavigationbooleanfalseDisables primitive and custom month / year navigation.
reverseMonthsbooleanfalseReverses visual month order without changing chronological navigation.
reverseYearsbooleanfalseReverses the custom year-panel option order.
defaultMonthDatecurrent monthInitial month displayed.
monthDate-Controlled visible month. Use with onMonthChange.
onMonthChange(month: Date) => void-Called when the visible month changes.
startMonthDatetoday − 100 yearsEarliest navigable month. Bounds the year panel.
endMonthDatetoday + 100 yearsLatest navigable month.
disabledMatcher | Matcher[]-Days matching the condition cannot be selected.
monthsClassNameClassValue-className for the months grid panel.
yearsClassNameClassValue-className for the years grid panel.
resetViewsKeynumber-Resets open panels to days when the token changes after mount.

Views

The calendar manages three internal views:

  1. Days (default) — the standard day grid. The caption shows the month name and year as clickable buttons.
  2. 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.
  3. 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, and hideNavigation are managed internally to drive the panel navigation, root marker, and date math. formatters.formatCaption is 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 the components/ui/calendar primitive directly.
  • labels.labelMonthDropdown and labels.labelYearDropdown name both the caption triggers and their panels. formatMonthDropdown / formatYearDropdown also apply to those custom surfaces and receive a locale-, numeral-, and time-zone-aware DateLib.
  • classNames.month_caption, styles.month_caption, and DayPicker caption animation data are preserved on the custom caption. month_grid customization still belongs to the days grid; use monthsClassName / yearsClassName for the two custom panels.
  • The month and year caption buttons replace the native <select> dropdowns that react-day-picker renders with captionLayout="dropdown". This gives a consistent, styled experience across platforms.
  • The year panel defaults to ±100 years from today when startMonth / endMonth are not provided.
  • startMonth / endMonth also bound the custom month panel, so out-of-range months are disabled before the day grid is shown.
  • Custom navigation follows DayPicker's disableNavigation, controlled month, 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.
  • resetViewsKey is edge-triggered: its initial value preserves defaultView; any later value change, including a change to 0, resets every visible slot to days.
  • When numberOfMonths > 1, clicking any month's caption enters the panel view. After selecting, all months update together.

On this page