Calendar

Calendar

A date grid built on react-day-picker. Supports single, multiple and range selection, and composes into date pickers inside a Popover.

Playground

October 2026
numberOfMonths
showOutsideDays
showWeekNumber
disableWeekends
const [date, setDate] = React.useState<Date | undefined>(new Date())

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

Preview

The default composition: pass mode="single" with a controlled selected date and an onSelect handler. The root renders as a floating card on a bg-popover surface with an elevation shadow.

October 2026
const [date, setDate] = React.useState<Date | undefined>(new Date())

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

Installation

npx shadcn@latest add https://espresso-base-ui.vercel.app/r/calendar.json

Usage

import { Calendar, CalendarDayButton } from "@/components/ui/calendar"
const [date, setDate] = React.useState<Date | undefined>(new Date())

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

Range selection

mode="range" selects a start and end date. The span between them gets a continuous bg-muted track while the endpoints fill with the primary color; add numberOfMonths to show months side by side.

August 2026
September 2026
const [range, setRange] = React.useState<DateRange | undefined>({
  from: new Date(2026, 7, 5),
  to: new Date(2026, 7, 14),
})

<Calendar
  mode="range"
  selected={range}
  onSelect={setRange}
  numberOfMonths={2}
  defaultMonth={range?.from}
  classNames={{
    months:
      "relative flex flex-row items-stretch [&>div+div]:border-l [&>div+div]:border-border [&>div]:py-2 [&>div]:px-2",
    nav: "absolute inset-x-2 top-2 flex w-auto items-center justify-between gap-1",
    root: "w-fit p-0!",
  }}
/>

Date picker in a popover

The most common real-world composition wraps the calendar in a Popover behind an outline Button trigger. Strip the calendar's own surface with border-0 shadow-none – inside a popover it already sits on a popover background.

<Popover>
  <PopoverTrigger
    render={
      <Button variant="outline" className="w-56 justify-start gap-2 font-normal">
        <CalendarIcon />
        {picked ? format(picked, "PPP") : "Pick a date"}
      </Button>
    }
  />
  <PopoverContent className="w-auto p-0" align="start" sideOffset={4}>
    <Calendar
      mode="single"
      selected={picked}
      onSelect={setPicked}
      className="border-0 shadow-none"
    />
  </PopoverContent>
</Popover>

Presets

Pair the calendar with quick-pick shortcuts in a bordered side column – ghost buttons that set the date and jump the visible month via a controlled month / onMonthChange.

const [date, setDate] = React.useState<Date | undefined>(new Date())
const [month, setMonth] = React.useState(new Date())

<PopoverContent className="w-auto p-0" align="start" sideOffset={4}>
  <div className="flex">
    <div className="flex flex-col items-start gap-px border-r border-border p-1">
      <Button variant="ghost" size="sm" className="text-sm" onClick={() => pick(addDays(new Date(), 1))}>
        Tomorrow
      </Button>
      <Button variant="ghost" size="sm" className="text-sm" onClick={() => pick(addDays(new Date(), 7))}>
        Next week
      </Button>
    </div>
    <Calendar
      mode="single"
      selected={date}
      onSelect={setDate}
      month={month}
      onMonthChange={setMonth}
      className="border-0 shadow-none"
    />
  </div>
</PopoverContent>

Disabled dates

The disabled prop takes any DayPicker matcher – an array of dates, a range, or a dayOfWeek rule. Disabled cells drop to 50% opacity and stop receiving pointer events.

October 2026
<Calendar
  mode="single"
  selected={date}
  onSelect={setDate}
  disabled={{ dayOfWeek: [0, 6] }}
/>

API reference

Calendar wraps DayPicker from react-day-picker and forwards every DayPicker prop – mode, selected, onSelect, month, onMonthChange, defaultMonth, locale, formatters, components, classNames and friends. The props below are the ones the wrapper defaults or adds.

Calendar

PropTypeDefaultDescription
mode"single" | "multiple" | "range"–Selection behavior (from react-day-picker). Pair with selected / onSelect for controlled state.
showOutsideDaysbooleantrueRender the leading and trailing days of adjacent months in muted text.
captionLayout"label" | "dropdown" | "dropdown-months" | "dropdown-years""label"Month caption style – a static label or native month/year dropdowns (short month names by default).
buttonVariantButton variant"ghost"Button variant applied to the previous/next month navigation buttons.
numberOfMonthsnumber1Months rendered side by side. With more than one, the caption centers instead of left-aligning.
showWeekNumberbooleanfalseAdds an ISO week-number column; the grid widens by one cell.
disabledMatcher | Matcher[]–DayPicker matcher(s) for un-selectable days: dates, ranges, or { dayOfWeek: [...] }.
localeLocale–date-fns locale; also drives the built-in short-month and narrow-weekday formatters.
ComponentDescription
CalendarRoot wrapper (data-slot="calendar"). Popover-style card surface that turns transparent inside card or popover content.
CalendarDayButtonThe day-cell Button used via the DayPicker components override. Exposes data-day, data-selected-single, data-range-start, data-range-middle and data-range-end for styling; auto-focuses when DayPicker marks it focused.

Styling hooks & accessibility

Cell geometry is driven by two CSS variables set on the root: --cell-size (default 1.5rem) and --cell-radius. Override them from className – e.g. [--cell-size:2.75rem] for a larger grid. Day buttons reflect their selection state as data attributes (data-selected-single, data-range-start, data-range-middle, data-range-end), and the navigation chevrons flip automatically in RTL layouts. Keyboard navigation, focus management and aria roles come from react-day-picker; nav buttons expose aria-disabled when no previous/next month is available.

<Calendar
  mode="single"
  className="[--cell-size:2.75rem]"
/>

Search documentation

Search the component docs