Pickers and overlays
Calendar
A month grid for choosing one day with the keyboard or a click.
When to use it: Use it when seeing the whole month helps, alone or inside a DatePicker.
Example
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
Selected: 2026-07-14
import { useState } from "react";
import {
Calendar,
CalendarCell,
CalendarGrid,
CalendarHeader,
CalendarNextButton,
CalendarPreviousButton,
} from "@comp0/react";
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/16/solid";
export function Example() {
const [date, setDate] = useState("2026-07-14");
return (
<Calendar
as="div"
className="max-w-xs rounded border border-zinc-950/10 bg-white p-3 dark:border-white/10 dark:bg-zinc-900"
value={date}
onChange={setDate}
min="2026-01-01"
max="2026-12-31"
>
<div className="flex items-center justify-between gap-2 pb-2">
<CalendarPreviousButton className="inline-flex size-8 items-center justify-center rounded text-zinc-700 outline-teal-600 hover:bg-zinc-100 focus-visible:outline-2 disabled:opacity-40 dark:text-zinc-300 dark:outline-teal-400 dark:hover:bg-zinc-800">
<ChevronLeftIcon className="size-4" aria-hidden="true" />
</CalendarPreviousButton>
<CalendarHeader className="text-base font-medium text-zinc-900 sm:text-sm dark:text-zinc-100" />
<CalendarNextButton className="inline-flex size-8 items-center justify-center rounded text-zinc-700 outline-teal-600 hover:bg-zinc-100 focus-visible:outline-2 disabled:opacity-40 dark:text-zinc-300 dark:outline-teal-400 dark:hover:bg-zinc-800">
<ChevronRightIcon className="size-4" aria-hidden="true" />
</CalendarNextButton>
</div>
<CalendarGrid className="w-full border-separate border-spacing-0.5 [&_th]:pb-1 [&_th]:text-sm [&_th]:font-normal [&_th]:text-zinc-500 dark:[&_th]:text-zinc-400">
{(cell) => (
<CalendarCell
date={cell.iso}
outsideMonth={cell.outsideMonth}
className="p-0 text-center [&>button]:size-9 [&>button]:rounded [&>button]:text-base [&>button]:text-zinc-800 [&>button]:outline-teal-600 [&>button]:hover:bg-zinc-100 [&>button]:focus-visible:outline-2 [&>button]:disabled:opacity-40 [&>button[data-outside-month]]:text-zinc-400 [&>button[data-selected]]:bg-teal-600 [&>button[data-selected]]:text-white [&>button[data-today]]:font-semibold sm:[&>button]:text-sm dark:[&>button]:text-zinc-100 dark:[&>button]:outline-teal-400 dark:[&>button]:hover:bg-zinc-800 dark:[&>button[data-outside-month]]:text-zinc-600 dark:[&>button[data-selected]]:bg-teal-500 dark:[&>button[data-selected]]:text-zinc-950"
/>
)}
</CalendarGrid>
<p className="pt-2 text-base text-zinc-600 sm:text-sm dark:text-zinc-400">Selected: {date}</p>
</Calendar>
);
}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.
Calendar
Selected-date and visible-month provider. Does not add a DOM element.
CalendarHeader
Live region announcing the visible month. Owns a DOM element.
CalendarPreviousButtonOptional
Native button that shows the previous month. Owns a DOM element.
CalendarNextButtonOptional
Native button that shows the next month. Owns a DOM element.
CalendarGrid
Month table with localized weekday headers. Owns a DOM element.
CalendarCellOptional
Day cell wrapping the real day button. Owns a DOM element.
Step by step
- 1
Add the main part
Wrap CalendarHeader and CalendarGrid in Calendar; the grid renders the month on its own.
- 2
Add the supporting parts
Add CalendarPreviousButton and CalendarNextButton around the header for month paging.
- 3
Make the behavior clear
Read the choice from onChange as an ISO date, and fence the range with min/max; arrow keys walk days, PageUp/PageDown walk months.
Exampletsx <Calendar defaultValue="2026-07-14" onChange={setDate}><CalendarHeader /><CalendarGrid /></Calendar>
Keyboard
- ←
- Moves focus one day back.
- →
- Moves focus one day forward.
- ↑
- Moves focus one week back.
- ↓
- Moves focus one week forward.
- Home
- Moves focus to the start of the week.
- End
- Moves focus to the end of the week.
- PgUp
- Shows the previous month.
- PgDn
- Shows the next month.
- ⇧PgUp
- Shows the previous year.
- ⇧PgDn
- Shows the next year.
- ↵
- Selects the focused day.
- Space
- Selects the focused day.
Forms and accessibility
Selection does not create a native form value; pair the calendar with a named DateField, or mirror the value into a hidden input.
Accessibility checklist
- The grid is one tab stop; arrow keys move between days, so keep custom cells as buttons.
- Month and weekday names come from Intl for the given locale, but the prev/next button labels default to English aria-labels; translate them.
- The header is a polite live region, so month changes are announced without moving focus.
API reference
import { Calendar, CalendarCell, CalendarGrid, CalendarHeader, CalendarNextButton, CalendarPreviousButton } from "@comp0/react";Calendar
Context onlySelected-date and visible-month provider.
| Prop | Type | Description |
|---|---|---|
value | string | Controlled date as "YYYY-MM-DD". |
defaultValue | string | Initial date as "YYYY-MM-DD". |
onChange | (value: string) => void | Receives the selected ISO date. |
min / max | string | Inclusive selectable range; days outside it are disabled. |
locale | string | BCP 47 tag for month and weekday names and the week start; defaults to the browser locale. |
as | ElementType | Renders a wrapper element; there is no DOM without it. |
CalendarHeader
DOM elementLive region announcing the visible month.
| Prop | Type | Description |
|---|---|---|
children | (label: string) => ReactNode | Custom content receiving the localized month-and-year label. |
CalendarPreviousButton
OptionalDOM elementNative button that shows the previous month.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Defaults to the English "Previous month"; pass a translation. |
CalendarNextButton
OptionalDOM elementNative button that shows the next month.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Defaults to the English "Next month"; pass a translation. |
CalendarGrid
DOM elementMonth table with localized weekday headers.
| Prop | Type | Description |
|---|---|---|
children | (cell: MonthMatrixCell) => ReactNode | Custom day cell renderer; defaults to a plain CalendarCell. |
CalendarCell
OptionalDOM elementDay cell wrapping the real day button.
| Prop | Type | Description |
|---|---|---|
date | string | This cell's date as "YYYY-MM-DD". |
outsideMonth | boolean | Marks a leading or trailing day from a neighboring month. |
children | ReactNode | Visible day content; defaults to the day number. |
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.
[data-selected]on CalendarCell
- The day is selected.
[data-today]on CalendarCell
- The day is today.
[data-outside-month]on CalendarCell
- The day belongs to a neighboring month.
[data-value]on Calendar (with as)
- A date is selected.