Gesso
Forms

Calendar

Month grid for due dates. react-day-picker v9, Phosphor carets.

Calendar is a month grid on the page for picking a day, a range, or several days. It is react-day-picker v9 drawn with the kit's ghost buttons and Phosphor carets, the chosen day fills with bg-primary, and today takes the bg-fill wash. To open it from a field and close it on the pick, use Date picker.

October 2026

Usage

'use client';

import * as React from 'react';
import { Calendar } from '@/components/ui/calendar';

export function DueDate() {
  const [date, setDate] = React.useState<Date | undefined>();
  return <Calendar mode="single" selected={date} onSelect={setDate} />;
}

The file exports Calendar and CalendarDayButton. Its onSelect is a function, so render it from a client component, as above. It paints its own bg-surface with 12px of padding, and inside a Popover it drops the fill and takes the popover's surface. On another surface, like a Card, pass className="bg-transparent" to do the same. Month and day names are in English unless you pass a locale from react-day-picker/locale.

Picking a day, a range, or several

mode sets what a click picks. "single" picks one day, and clicking the chosen day again clears it unless required is set. "multiple" adds or removes each day you click, and "range" picks a { from, to } range whose ends fill with bg-primary and whose days in between take a bg-fill band. Without a mode the days are plain text and nothing can be picked.

October 2026
November 2026

numberOfMonths shows more than one month. They sit side by side from the md breakpoint up and stack below it. Days from the months before and after fill out the first and last weeks in muted text, which showOutsideDays={false} turns off.

Limiting the days

disabled takes react-day-picker matchers, like { before: new Date() } for past days or { dayOfWeek: [0, 6] } for weekends. The days it matches drop to 64% opacity, cannot be picked, and are skipped by the arrow keys. startMonth and endMonth stop the month arrows at a bound, and an arrow with nowhere to go dims and leaves the tab order. Below, past days and weekends are off, and the calendar cannot go back before this month.

October 2026

Month and year menus

captionLayout="dropdown" swaps the caption for a month menu and a year menu, so a date years away is two picks rather than a long run of clicks. "dropdown-months" and "dropdown-years" show one of the two. Each menu is a native <select> under the caption's text, and the years run from 100 years back to the end of this year unless you set startMonth and endMonth. The month menu uses short month names in the calendar's locale, so the server and the browser render the same names.

October 2026

Props

Calendar takes every react-day-picker DayPicker prop, and adds or changes these.

PropTypeDefaultWhat it does
buttonVariant"default" | "secondary" | "outline" | "ghost" | "destructive" | "link""ghost"The variant of the previous and next month buttons. Days stay ghost.
showOutsideDaysbooleantrueShows the days of the months around this one in muted text. react-day-picker defaults it to false.

classNames, components, and formatters are spread over the kit's, so a key you pass replaces the kit's for that part and the rest stay as they are. CalendarDayButton is the kit's day, exported so a DayButton of your own can wrap it. The rest are react-day-picker's, like mode, selected, onSelect, required, disabled, startMonth, endMonth, numberOfMonths, weekStartsOn, locale, footer, and autoFocus, and its docs cover them.

Accessibility

  • Each month is a grid named by its month and year, with each weekday header named in full, like "Sunday", and each day a gridcell. A day reads as its full date, with "Today" before it and "selected" after it when they apply, like "Today, Thursday, October 1st, 2026, selected".
  • Only one day is in the tab order: the day you last focused, or else the chosen day, or else today, or else the first day that can be picked. Tab reaches the month buttons first and then that day.
  • On a day, ← and → move a day, ↑ and ↓ move a week, and Home and End go to the start and end of the week. PageUp and PageDown move a month, or a year with Shift, and Shift with ← and → moves a month and with ↑ and ↓ a year. When focus moves into another month, the calendar turns to it. Enter or Space picks the focused day. With dir="rtl" the grid and the month buttons mirror, their arrows point the reading way, and ← and → swap.
  • The month caption is a polite live region, so a screen reader hears the new month when it changes. A footer is one too, for a line like "You picked October 1".
  • The month buttons are named "Go to the Previous Month" and "Go to the Next Month". At a startMonth or endMonth bound the button is aria-disabled and leaves the tab order.
  • A disabled day is a disabled button, and the arrow keys skip it. mode="multiple" and mode="range" mark the grid aria-multiselectable.
  • The focused day shows a 2px ring, and in forced colors it keeps an outline that the system paints.
  • The chosen day, a range, and today are marked only by fills, which forced colors repaints, so in Windows high contrast they look like any other day and only their names say which they are.
  • On a touch screen each day grows to 44px square and the month buttons to 44px tall, so the calendar takes more room.
  • The grid does not animate when the month changes, and under reduced motion the days change color on hover without a transition.

When to use something else

  • Date picker for a date field in a form. It opens this calendar in a Popover from a button that shows the date, and closes it on the pick. It picks one day, so for a range or several days behind a button, put a Calendar in a Popover yourself, as the Date picker page shows.

Install

shadcn add @gesso/calendar

It brings Button for the days and the month buttons, and installs react-day-picker and date-fns.

On this page