Morpha UI

Date Picker

A date picker built from the Popover and the Calendar, with range, dropdown captions, typed input and presets.

Overview

The date picker is a composition, not a new primitive: a Popover holding a Calendar. The kit ships the two pieces that glue them together, so the field half matches the rest of the text-field family. DatePickerTrigger is a SelectTrigger in every way that shows — the same 56px frame whose outline ring carries state (hover, focus, open, invalid, disabled), a calendar glyph in the trailing slot, and the placeholder in the muted tone until a date lands. DatePickerContent is the shared popup surface with the popover's padding and fixed width stripped, so the grid sizes itself.

Formatting stays with you: the trigger renders whatever label you hand it, so date-fns, Intl.DateTimeFormat or a hand-rolled string all work.

$ npx morpha-ui add date-picker

Examples

Range

Keep the grid in mode="range" and label the trigger with both endpoints. The popup stays open while the second endpoint is still missing.

Date of birth

captionLayout="dropdown" turns the month and year captions into selects, and startMonth/endMonth bound the range — jumping back three decades beats 400 clicks on the previous-month arrow.

Input

When typing is the primary path, drop DatePickerTrigger and render the PopoverTrigger as an InputGroupButton inside an InputGroup: the field takes the keystrokes, the button opens the grid. Parse on change with parse + isValid, and keep the calendar's month in state so it follows what was typed. opens the popup from the keyboard.

Date and time

Pair the picker with a native type="time" input inside an InputGroup — the browser's own control handles the clock half, keeping the platform's locale and keyboard behaviour, with no second popup.

Presets

Put a Select of shortcuts above the grid inside DatePickerContent: the common answers become one click, and the calendar stays there for everything else.

Installation

Morpha UI components are distributed via the Morpha CLI: npx morpha-ui add date-picker. It pulls in the calendar and popover items it composes.

npx morpha-ui add date-picker

Usage

import { format } from "date-fns"

import { Calendar } from "@/components/ui/calendar"
import {
  DatePicker,
  DatePickerContent,
  DatePickerTrigger,
} from "@/components/ui/date-picker"
const [open, setOpen] = React.useState(false)
const [date, setDate] = React.useState<Date | undefined>()

return (
  <DatePicker open={open} onOpenChange={setOpen}>
    <DatePickerTrigger>{date && format(date, "PPP")}</DatePickerTrigger>
    <DatePickerContent>
      <Calendar
        mode="single"
        selected={date}
        onSelect={(value) => {
          setDate(value)
          setOpen(false)
        }}
        autoFocus
      />
    </DatePickerContent>
  </DatePicker>
)

Hand the trigger the formatted date as its children; leave it empty and the placeholder shows instead. Closing on select is your call — a range picker wants to stay open, a single-date picker does not.

API Reference

DatePicker

Re-export of the Popover root — open, defaultOpen, onOpenChange, modal.

Prop

Type

DatePickerTrigger

Prop

Type

DatePickerContent

Takes the props of PopoverContent (align, side, sideOffset, alignOffset), defaulting align to start and dropping the popover's w-72 p-4.

Accessibility

  • The trigger is a real button: Enter / Space opens the popup, Esc closes it and returns focus to the trigger — the Popover primitive handles the focus trip.
  • Pass autoFocus to the Calendar so the grid takes focus on open; from there Arrow keys move by day and Page Up/Down by month.
  • The trigger carries no visible text of its own when empty — give it a Label (via id/htmlFor) or an aria-label naming the field.
  • Icon-only triggers, like the InputGroupButton in the input example, need an aria-label (Open calendar).
  • Errors go through aria-invalid, which recolors the ring to error.

On this page