Calendar

A month grid for picking one date, several dates, or a range. Use it on its own in a form, or inside a popover as a date picker.

October 2026

Monday, October 12, 2026

Usage

Calendar usageTSX
import { useState } from "react";
import { Calendar } from "@tulipfarm/sprout/calendar";

const [date, setDate] = useState<Date | undefined>(new Date(2026, 9, 12));

<Calendar
  mode="single"
  selected={date}
  onSelect={setDate}
  defaultMonth={new Date(2026, 9, 1)}
/>

Calendar wraps React DayPicker 10 and takes all of its props. Sprõut adds its own styles, Sprõut buttons for month navigation, and Sprõut selects for the month and year menus.

Selection modes

Set mode to "single", "range", or "multiple", and control the value with selected and onSelect. A range passes { from, to }; the first and last days are filled and the days between share a band. Pass a matcher to disabled, such as { dayOfWeek: [0, 6] } for weekends.

Selection modes

Click a selected date again to clear it. Weekends are disabled in the multiple-date calendar.

Single datemode="single" · ghost nav · label caption · Sunday first
October 2026

Monday, October 12, 2026

Date rangemode="range" · Oct 12 – 16 · start, middle, and end days
October 2026

Oct 12 – Oct 16

Multiple datesmode="multiple" · disabled={{ dayOfWeek: [0, 6] }}
October 2026

Oct 13, Oct 15

Captions and navigation

captionLayout="label" (default) shows the month name. "dropdown" shows month and year menus; "dropdown-months" and "dropdown-years" show one. buttonVariant picks the Button variant for the previous and next buttons: "ghost" (default) or "outline".

Captions and navigation

Month and year menus are Sprõut selects, never native ones. Years run from 100 years ago to this year.

Month labelcaptionLayout="label" · buttonVariant="ghost" (defaults)
October 2026
Dropdown captionscaptionLayout="dropdown" · buttonVariant="outline"
October 2026

Date picker

Put the calendar straight inside PopoverContent. It fills the panel and drops its own surface. Close the popover in onSelect and ignore undefined so clicking the chosen day again doesn’t clear it.

Calendar date pickerTSX
<Popover open={open} onOpenChange={setOpen}>
  <h2 aria-label={title}>
    <PopoverTrigger
      render={<Button variant="ghost" size="calendar-title" aria-label="Choose date" />}
    >
      {title}
      <ChevronDown size={14} aria-hidden="true" />
    </PopoverTrigger>
  </h2>
  <PopoverContent align="start" aria-label="Choose a calendar date">
    <Calendar
      mode="single"
      weekStartsOn={1}
      selected={day}
      defaultMonth={day}
      onSelect={(date) => {
        if (date) {
          setOpen(false);
          show(date);
        }
      }}
    />
  </PopoverContent>
</Popover>

Date picker

The calendar page opens a Monday-first calendar from its month title. Picking a date closes the popover and moves the view.

Toolbar titleghost calendar-title trigger · PopoverContent align start · weekStartsOn 1

Showing the week of Monday, October 19, 2026.

Layout and custom days

numberOfMonths shows months side by side from 768px and stacked below. showWeekNumber adds a week column, and weekStartsOn sets the first weekday. To add content under a date, render CalendarDayButton from components.DayButton and put the extra content in a span. Set --sprout-calendar-cell to make room.

Layout and custom days

Show week numbers and two months for longer ranges. Add content under each date with CalendarDayButton.

Out of officenumberOfMonths 2 · showWeekNumber · weekStartsOn 1 · range across months
October 2026
40
41
42
43
44
November 2026
44
45
46
47
48
49

Oct 26 – Nov 6

Event countscomponents.DayButton → CalendarDayButton · --sprout-calendar-cell 2.25rem
October 2026

Monday, October 12, 2026 · 3 events

API

Calendar takes every React DayPicker prop. Sprõut sets showOutsideDays to true and captionLayout to "label", and adds buttonVariant ("ghost"). Your classNames, formatters, and components merge over Sprõut’s. The root has data-slot="calendar".

CalendarDayButton is the day button. It takes the DayPicker day button props and sets data-selected-single, data-range-start, data-range-middle, data-range-end, and data-day. The package also exports the DateRange and Matcher types.

Accessibility

The month is a labelled grid. Tab moves to the selected day, or today. Arrow keys move by day and week, Home and End go to the start and end of the week, Page Up and Page Down change month, and Shift with Page Up or Page Down changes year. Enter or Space selects. Disabled days stay readable but can’t be chosen.

Day colors change over 120ms, with no motion when reduced motion is requested. In forced-colors mode selected days keep an outline.

Theme tokens

Selected days use --sprout-primary and --sprout-on-primary, so they follow the TulipFarm brand when you opt in. Today and range bands use --sprout-hover; muted text uses --sprout-muted. Set --sprout-calendar-cell (1.75rem) and --sprout-calendar-radius on the calendar to resize it.