Skip to content

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.

On this page

Example

Date Picker.tsxtsx
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.

  1. DatePicker

    Shared date value, open state, and field-context provider. Does not add a DOM element.

  2. Label

    Visible name connected to the DateField. Owns a DOM element.

  3. DateFieldOptional

    Native date input that reads and writes the picker's value. Owns a DOM element.

  4. DatePickerTrigger

    Native button that opens the calendar. Owns a DOM element.

  5. DatePickerPopover

    The calendar dialog surface. Owns a DOM element.

  6. Calendar

    Month grid that adopts the picker's value and closes it on selection. Owns a DOM element.

Step by step

  1. 1

    Add the main part

    Put a Label, DateField, and DatePickerTrigger inside DatePicker; hide the browser-owned indicator so only the custom trigger is visible.

  2. 2

    Add the supporting parts

    Put a Calendar with CalendarHeader and CalendarGrid inside DatePickerPopover; it adopts the shared value automatically.

  3. 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 DatePickerTrigger, you must hide the browser-owned indicator so only one picker button is visible; keep the native DateField editable.

API reference

Importtsx
import { Calendar, CalendarGrid, CalendarHeader, DateField, DatePicker, DatePickerPopover, DatePickerTrigger, Label } from "@comp0/react";

DatePicker

Context only

Shared date value, open state, and field-context provider.

PropTypeDescription
valuestringControlled date as "YYYY-MM-DD".
defaultValuestringInitial date as "YYYY-MM-DD".
onChange(value: string) => voidReceives the selected ISO date.
openbooleanControlled open state of the calendar.
defaultOpenbooleanInitial open state of the calendar.
onToggle(open: boolean) => voidReceives the next open state.
namestringSubmission name for the selected ISO date.
formstringAssociates the date value with a form by id.
disabledbooleanField-wide states.
invalidbooleanField-wide states.
requiredbooleanField-wide states.
asElementTypeRenders a wrapper element; there is no DOM without it.

Label

DOM element

Visible name connected to the DateField.

DateField

OptionalDOM element

Native date input that reads and writes the picker's value.

PropTypeDescription
namestringSubmission name; the value submits as YYYY-MM-DD.

DatePickerTrigger

DOM element

Native button that opens the calendar.

PropTypeDescription
aria-labelstringDefaults to the English "Choose date"; pass a translation.
disabledbooleanDisables opening.

DatePickerPopover

DOM element

The calendar dialog surface.

PropTypeDescription
aria-labelstringDefaults to the English "Calendar"; pass a translation.
placementPopoverPlacementTrigger side to open on, such as "bottom end"; flips when there is no room.
offsetnumberPixel gap between the trigger and the surface.

Calendar

DOM element

Month 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.

DatePicker

Style hookMeaning
[data-value]A date is selected.

DatePickerTrigger

Style hookMeaning
[data-open]The calendar is open.

DatePickerPopover

Style hookMeaning
[data-open]The calendar is open.
:popover-openNative pseudo-class equivalent.

Keep exploring