DatePicker

A segmented date field with a calendar popover, built on React Aria. Users can type the date or pick it from the grid; both write to the same value.

import { CalendarDate } from "@internationalized/date"
import { Calendar, DatePicker } from "@pockets-ui/components"

Anatomy

PartRole
DatePicker.RootOwns the value and bounds.
DatePicker.LabelThe field's visible label.
DatePicker.ControlWraps the input and the popover trigger.
DatePicker.InputThe segmented date input.
DatePicker.TriggerThe button that opens the calendar.
DatePicker.ContentThe popover. Put a Calendar inside.

The calendar isn't built in — you compose one into DatePicker.Content, and it inherits the picker's value and bounds through context, so it needs no props of its own.

Basic

Event date
Code
import { Calendar, DatePicker } from "@pockets-ui/components"

export default function DatePickerBasic() {
  return (
    <DatePicker.Root>
      <DatePicker.Label>Event date</DatePicker.Label>
      <DatePicker.Control>
        <DatePicker.Input />
        <DatePicker.Trigger />
      </DatePicker.Control>
      <DatePicker.Content>
        <Calendar.Root>
          <Calendar.ActionRow>
            <Calendar.ActionButton slot="previous">‹</Calendar.ActionButton>
            <Calendar.Heading />
            <Calendar.ActionButton slot="next">›</Calendar.ActionButton>
          </Calendar.ActionRow>
          <Calendar.Grid>
            <Calendar.Header>
              {(day) => <Calendar.HeaderCell>{day}</Calendar.HeaderCell>}
            </Calendar.Header>
            <Calendar.Body>
              {(date) => <Calendar.Cell date={date} />}
            </Calendar.Body>
          </Calendar.Grid>
        </Calendar.Root>
      </DatePicker.Content>
    </DatePicker.Root>
  )
}

Default value

Launch day
Code
import { CalendarDate } from "@internationalized/date"
import { DatePicker } from "@pockets-ui/components"
import { PickerCalendar } from "./shared"

export default function DatePickerDefaultValue() {
  return (
    <DatePicker.Root defaultValue={new CalendarDate(2026, 7, 19)}>
      <DatePicker.Label>Launch day</DatePicker.Label>
      <DatePicker.Control>
        <DatePicker.Input />
        <DatePicker.Trigger />
      </DatePicker.Control>
      <DatePicker.Content>
        <PickerCalendar />
      </DatePicker.Content>
    </DatePicker.Root>
  )
}

Min / max range

Bounds set on the root apply to both halves at once: the calendar greys out the excluded days and the segmented input refuses to type past them.

Appointment (next month only)
Code
import { getLocalTimeZone, today } from "@internationalized/date"
import { DatePicker } from "@pockets-ui/components"
import { PickerCalendar } from "./shared"

export default function DatePickerMinMax() {
  return (
    <DatePicker.Root
      minValue={today(getLocalTimeZone())}
      maxValue={today(getLocalTimeZone()).add({ months: 1 })}
    >
      <DatePicker.Label>Appointment (next month only)</DatePicker.Label>
      <DatePicker.Control>
        <DatePicker.Input />
        <DatePicker.Trigger />
      </DatePicker.Control>
      <DatePicker.Content>
        <PickerCalendar />
      </DatePicker.Content>
    </DatePicker.Root>
  )
}

Disabled

Frozen date
Code
import { CalendarDate } from "@internationalized/date"
import { DatePicker } from "@pockets-ui/components"
import { PickerCalendar } from "./shared"

export default function DatePickerDisabled() {
  return (
    <DatePicker.Root isDisabled defaultValue={new CalendarDate(2026, 7, 19)}>
      <DatePicker.Label>Frozen date</DatePicker.Label>
      <DatePicker.Control>
        <DatePicker.Input />
        <DatePicker.Trigger />
      </DatePicker.Control>
      <DatePicker.Content>
        <PickerCalendar />
      </DatePicker.Content>
    </DatePicker.Root>
  )
}