Skip to content

Pickers and overlays

Range Calendar

A month grid that selects a start and end date as one range.

When to use it: Use it when seeing the span between two dates matters, alone or inside a Date Range Picker.

On this page

Example

July 2026
SMTWTFS
Range Calendar.tsxtsx
import {
  CalendarHeader,
  CalendarNextButton,
  CalendarPreviousButton,
  RangeCalendar,
  RangeCalendarCell,
  RangeCalendarGrid,
} from "@comp0/react";
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/20/solid";

export function Example() {
  return (
    <RangeCalendar defaultValue={["2026-07-14", "2026-07-18"]}>
      <div className="mb-2 flex items-center justify-between">
        <CalendarPreviousButton className="rounded p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800">
          <ChevronLeftIcon className="size-4" aria-hidden="true" />
        </CalendarPreviousButton>
        <CalendarHeader className="text-sm font-semibold" />
        <CalendarNextButton className="rounded p-2 hover:bg-zinc-100 dark:hover:bg-zinc-800">
          <ChevronRightIcon className="size-4" aria-hidden="true" />
        </CalendarNextButton>
      </div>
      <RangeCalendarGrid className="border-separate border-spacing-0 text-sm [&_button]:size-9 [&_button]:outline-teal-600 [&_button:focus-visible]:rounded [&_button:focus-visible]:outline-2 [&_button:hover]:rounded [&_button:hover]:bg-zinc-100 [&_td]:p-0 [&_td[data-in-range]]:bg-teal-50 [&_td[data-range-end]]:bg-teal-600 [&_td[data-range-end]_button]:text-white [&_td[data-range-preview]]:bg-teal-100 [&_td[data-range-start]]:bg-teal-600 [&_td[data-range-start]_button]:text-white dark:[&_button]:outline-teal-400 dark:[&_button:hover]:bg-zinc-800 dark:[&_td[data-in-range]]:bg-teal-950 dark:[&_td[data-range-preview]]:bg-teal-900">
        {(cell) => <RangeCalendarCell date={cell.iso} outsideMonth={cell.outsideMonth} />}
      </RangeCalendarGrid>
    </RangeCalendar>
  );
}

Anatomy

Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.

A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.

  1. RangeCalendar

    Start-and-end date state provider sharing Calendar month navigation. Does not add a DOM element.

  2. CalendarHeader

    Polite live region announcing the visible month. Owns a DOM element.

  3. CalendarPreviousButton / CalendarNextButtonOptional

    Month paging controls shared with Calendar. Owns a DOM element.

  4. RangeCalendarGrid

    Month grid with aria-multiselectable and a range cell renderer. Owns a DOM element.

  5. RangeCalendarCellOptional

    Day cell that marks selected endpoints and interior dates. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Wrap RangeCalendarGrid and the shared calendar header controls in RangeCalendar.

  2. 2

    Add the supporting parts

    Use RangeCalendarCell when custom day styling needs range start, end, and interior states.

  3. 3

    Make the behavior clear

    Read onChange as a [start, end] ISO pair; the first selection starts the range and the second completes it.

    Exampletsx
    <RangeCalendar defaultValue={["2026-07-14", "2026-07-18"]}>
      <CalendarHeader />
      <RangeCalendarGrid />
    </RangeCalendar>;

Keyboard

Moves focus one day backward or forward.
Moves focus one week backward or forward.
HomeEnd
Moves focus to the start or end of the week.
PgUpPgDn
Shows the previous or next month.
Space
Starts or completes the range at the focused day.

Forms and accessibility

RangeCalendar does not create form values; pair it with Date Range Picker or mirror the two ISO dates into form controls.

Accessibility checklist

  • The grid is one tab stop; retain the day buttons when customizing cells so arrow-key navigation keeps working.
  • Translate the default English previous and next month button labels for localized apps.
  • Style the start, end, and interior states distinctly so the selected span is understandable without color alone.

API reference

Importtsx
import { CalendarHeader, CalendarNextButton, CalendarPreviousButton, RangeCalendar, RangeCalendarCell, RangeCalendarGrid } from "@comp0/react";

RangeCalendar

Context only

Start-and-end date state provider sharing Calendar month navigation.

PropTypeDescription
value[string, string]Controlled [start, end] ISO dates; an empty end is incomplete.
defaultValue[string, string]Initial [start, end] ISO dates; an empty end is incomplete.
onChange(value: DateRange) => voidReceives the next [start, end] ISO date pair.
minstringInclusive selectable ISO date bounds.
maxstringInclusive selectable ISO date bounds.
localestringBCP 47 locale for month and weekday names.
disabledbooleanDisables every day and navigation control.

CalendarHeader

DOM element

Polite live region announcing the visible month.

CalendarPreviousButton / CalendarNextButton

OptionalDOM element

Month paging controls shared with Calendar.

RangeCalendarGrid

DOM element

Month grid with aria-multiselectable and a range cell renderer.

PropTypeDescription
children(cell: MonthMatrixCell) => ReactNodeCustom renderer; defaults to RangeCalendarCell.

RangeCalendarCell

OptionalDOM element

Day cell that marks selected endpoints and interior dates.

PropTypeDescription
datestringCell date as "YYYY-MM-DD".
outsideMonthbooleanMarks a leading or trailing day from a neighboring month.

Style hooks

Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.

RangeCalendarCell

Style hookMeaning
[data-range-start]The first selected day.
[data-range-end]The final selected day.
[data-in-range]A day falls strictly inside a completed range.
[data-range-preview]A day falls inside the provisional range while choosing its endpoint.
[data-outside-month]The day belongs to a neighboring month.

Keep exploring