Pickers and overlays
Date Picker
A typed date input with a calendar popover sharing one value.
When to use it: Use it when people know some dates by heart and browse for others.
Example
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
import { Label } from "@comp0/react";
import {
Calendar,
CalendarGrid,
CalendarHeader,
CalendarNextButton,
CalendarPreviousButton,
DateField,
DatePicker,
DatePickerPopover,
DatePickerTrigger,
} from "@comp0/react";
import { CalendarIcon, ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/16/solid";
export function Example() {
return (
<DatePicker as="div" className="flex max-w-xs flex-col gap-1.5" defaultValue="2026-07-14">
<Label className="text-base font-medium text-zinc-900 sm:text-sm dark:text-zinc-100">
Reservation date
</Label>
<div className="flex gap-1.5">
<div className="w-full overflow-hidden rounded border border-zinc-950/10 bg-white outline-teal-600 focus-within:outline-2 dark:border-white/10 dark:bg-zinc-900 dark:outline-teal-400">
<DateField className="w-[calc(100%+2.5rem)] border-0 bg-transparent px-3 py-2.5 text-base text-zinc-950 outline-none sm:py-2 sm:text-sm dark:text-zinc-50 [&::-webkit-calendar-picker-indicator]:hidden" />
</div>
<DatePickerTrigger className="rounded border border-zinc-950/10 bg-white px-3 text-zinc-700 outline-teal-600 focus-visible:outline-2 data-open:bg-zinc-100 dark:border-white/10 dark:bg-zinc-900 dark:text-zinc-300 dark:outline-teal-400 dark:data-open:bg-zinc-800">
<CalendarIcon className="size-4" aria-hidden="true" />
</DatePickerTrigger>
</div>
<DatePickerPopover
placement="bottom end"
offset={4}
className="rounded border-0 bg-white p-3 shadow-lg ring-1 ring-zinc-950/10 dark:bg-zinc-900 dark:shadow-none dark:ring-white/10"
>
<Calendar>
<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 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 dark:text-zinc-300 dark:outline-teal-400 dark:hover:bg-zinc-800">
<ChevronRightIcon className="size-4" aria-hidden="true" />
</CalendarNextButton>
</div>
<CalendarGrid className="border-separate border-spacing-0.5 [&_td]:p-0 [&_td]:text-center [&_td_button]:size-9 [&_td_button]:rounded [&_td_button]:text-base [&_td_button]:text-zinc-800 [&_td_button]:outline-teal-600 [&_td_button]:hover:bg-zinc-100 [&_td_button]:focus-visible:outline-2 [&_td_button[data-outside-month]]:text-zinc-400 [&_td_button[data-selected]]:bg-teal-600 [&_td_button[data-selected]]:text-white sm:[&_td_button]:text-sm dark:[&_td_button]:text-zinc-100 dark:[&_td_button]:outline-teal-400 dark:[&_td_button]:hover:bg-zinc-800 dark:[&_td_button[data-outside-month]]:text-zinc-600 dark:[&_td_button[data-selected]]:bg-teal-500 dark:[&_td_button[data-selected]]:text-zinc-950 [&_th]:pb-1 [&_th]:text-sm [&_th]:font-normal [&_th]:text-zinc-500 dark:[&_th]:text-zinc-400" />
</Calendar>
</DatePickerPopover>
</DatePicker>
);
}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.
DatePicker
Shared date value, open state, and field-context provider. Does not add a DOM element.
Label
Visible name connected to the DateField. Owns a DOM element.
DateFieldOptional
Native date input that reads and writes the picker's value. Owns a DOM element.
DatePickerTrigger
Native button that opens the calendar. Owns a DOM element.
DatePickerPopover
The calendar dialog surface. Owns a DOM element.
Calendar
Month grid that adopts the picker's value and closes it on selection. Owns a DOM element.
Step by step
- 1
Add the main part
Put a Label, a DateField, and a DatePickerTrigger inside DatePicker.
- 2
Add the supporting parts
Put a Calendar with CalendarHeader and CalendarGrid inside DatePickerPopover; it adopts the shared value automatically.
- 3
Make the behavior clear
Read the value from onChange as an ISO date; picking a day closes the popover and refills the DateField, and typing updates the calendar.
Exampletsx <DatePicker defaultValue="2026-07-14"><Label>Reservation</Label><DateField /><DatePickerTrigger /><DatePickerPopover><Calendar><CalendarHeader /><CalendarGrid /></Calendar></DatePickerPopover></DatePicker>
Keyboard
- ↵
- Opens the calendar from the trigger; selects the focused day inside.
- Space
- Opens the calendar from the trigger; selects the focused day inside.
- Esc
- Closes the calendar and returns focus to the trigger.
- ←→↑↓
- Move between days in the open calendar.
Forms and accessibility
The nested DateField owns form participation: give it a name and the shared value submits as YYYY-MM-DD with native validation.
Accessibility checklist
- Keep the DateField: typing a date must stay possible without opening the calendar.
- The trigger and surface default to English aria-labels ("Choose date", "Calendar"); translate them for localized apps.
- Opening moves focus to the focused day and closing returns it to the trigger; Escape always closes without changing the value.
- When adding a separate trigger, keep only one picker button visible; the example clips the browser-owned indicator without removing the native DateField.
API reference
import { Calendar, CalendarGrid, CalendarHeader, DateField, DatePicker, DatePickerPopover, DatePickerTrigger, Label } from "@comp0/react";DatePicker
Context onlyShared date value, open state, and field-context 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. |
open / defaultOpen | boolean | Controlled or initial open state of the calendar. |
onToggle | (open: boolean) => void | Receives the next open state. |
name | string | Submission name for the selected ISO date. |
form | string | Associates the date value with a form by id. |
disabled / invalid / required | boolean | Field-wide states. |
as | ElementType | Renders a wrapper element; there is no DOM without it. |
Label
DOM elementVisible name connected to the DateField.
DateField
OptionalDOM elementNative date input that reads and writes the picker's value.
| Prop | Type | Description |
|---|---|---|
name | string | Submission name; the value submits as YYYY-MM-DD. |
DatePickerTrigger
DOM elementNative button that opens the calendar.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Defaults to the English "Choose date"; pass a translation. |
disabled | boolean | Disables opening. |
DatePickerPopover
DOM elementThe calendar dialog surface.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Defaults to the English "Calendar"; pass a translation. |
placement | PopoverPlacement | Trigger side to open on, such as "bottom end"; flips when there is no room. |
offset | number | Pixel gap between the trigger and the surface. |
Calendar
DOM elementMonth grid that adopts the picker's value and closes it on selection.
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-open]on DatePickerTrigger, DatePickerPopover
- The calendar is open.
:popover-openon DatePickerPopover
- Native pseudo-class equivalent.
[data-value]on DatePicker (with as)
- A date is selected.