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.
Example
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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.
RangeCalendar
Start-and-end date state provider sharing Calendar month navigation. Does not add a DOM element.
CalendarHeader
Polite live region announcing the visible month. Owns a DOM element.
CalendarPreviousButton / CalendarNextButtonOptional
Month paging controls shared with Calendar. Owns a DOM element.
RangeCalendarGrid
Month grid with aria-multiselectable and a range cell renderer. Owns a DOM element.
RangeCalendarCellOptional
Day cell that marks selected endpoints and interior dates. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap RangeCalendarGrid and the shared calendar header controls in RangeCalendar.
- 2
Add the supporting parts
Use RangeCalendarCell when custom day styling needs range start, end, and interior states.
- 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
import { CalendarHeader, CalendarNextButton, CalendarPreviousButton, RangeCalendar, RangeCalendarCell, RangeCalendarGrid } from "@comp0/react";RangeCalendar
Context onlyStart-and-end date state provider sharing Calendar month navigation.
| Prop | Type | Description |
|---|---|---|
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) => void | Receives the next [start, end] ISO date pair. |
min | string | Inclusive selectable ISO date bounds. |
max | string | Inclusive selectable ISO date bounds. |
locale | string | BCP 47 locale for month and weekday names. |
disabled | boolean | Disables every day and navigation control. |
CalendarHeader
DOM elementPolite live region announcing the visible month.
CalendarPreviousButton / CalendarNextButton
OptionalDOM elementMonth paging controls shared with Calendar.
RangeCalendarGrid
DOM elementMonth grid with aria-multiselectable and a range cell renderer.
| Prop | Type | Description |
|---|---|---|
children | (cell: MonthMatrixCell) => ReactNode | Custom renderer; defaults to RangeCalendarCell. |
RangeCalendarCell
OptionalDOM elementDay cell that marks selected endpoints and interior dates.
| Prop | Type | Description |
|---|---|---|
date | string | Cell date as "YYYY-MM-DD". |
outsideMonth | boolean | Marks 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 hook | Meaning |
|---|---|
[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. |