Date Picker

A sealed <DatePicker /> composed internally from Popover + Button + Calendar. Prefer this over <Input type="date" /> for consistent theming, keyboard nav, and a11y.

Basic Date Picker

Date Range Picker

Select a range of dates for filtering or scheduling.

With Presets

Add quick selection presets for common date ranges.

Usage

import { DatePicker, DateRangePicker } from "@gradeui/ui"

const [date, setDate] = React.useState<Date>()
const [range, setRange] = React.useState<DateRange>()

<DatePicker value={date} onChange={setDate} />

<DateRangePicker
  value={range}
  onChange={setRange}
  numberOfMonths={2}
/>

Compose your own

Need a different trigger, custom positioning, or a non-standard layout? Drop the sealed DatePicker and reach for the primitives directly — Calendar and Popover are exported from the same barrel.

import { Calendar, Popover, PopoverTrigger, PopoverContent, Button } from "@gradeui/ui"
import { format } from "date-fns"
import { Calendar as CalendarIcon } from "lucide-react"

<Popover>
  <PopoverTrigger asChild>
    <Button variant="outline">
      <CalendarIcon className="mr-2 h-4 w-4" />
      {date ? format(date, "PPP") : "Pick a date"}
    </Button>
  </PopoverTrigger>
  <PopoverContent className="w-auto p-0">
    <Calendar mode="single" selected={date} onSelect={setDate} initialFocus />
  </PopoverContent>
</Popover>

Sidecar

The Markdown sidecar Studio (and the Grade MCP server, when it ships) reads to understand this component — frontmatter, when- to-use guidance, and canonical examples. Authored once at packages/ui/components/ui/date-picker.md and shipped inside the published @gradeui/ui tarball.

---
name: DatePicker
import: "@gradeui/ui"
props:
  - value?: Date (single) | DateRange (range)
  - onChange?: (value) => void — called on select or clear
  - placeholder?: string — trigger label when empty (default "Pick a date" / "Pick a date range")
  - disabled?: boolean
  - format?: string — date-fns format token for the trigger label (default "PPP" single, "LLL dd, y" range)
  - align?: "start" | "center" | "end" — popover align (default "start")
  - side?: "top" | "right" | "bottom" | "left" — popover side
  - captionLayout?: "label" | "dropdown" | "dropdown-months" | "dropdown-years"
  - className?: string — on the trigger button
  - contentClassName?: string — on the PopoverContent
  - icon?: ReactNode — replaces the default CalendarIcon
  - numberOfMonths?: number — DateRangePicker only, default 2
when_to_use: Any date or date-range entry. Use DatePicker for a single date (DOB, due date, booking). Use DateRangePicker for a span (report period, stay dates, filter window). Prefer these over <Input type="date"> — consistent theming, keyboard nav, a11y, and no browser-native UI drift.
composes_with: [Label, Form, Card (in CardContent), Button (form submit)]
subcomponents: [DateRangePicker]
aliases: [datepicker, calendar input, date field, date range, datepickerios, react native date picker, calendar input field, date field control]
---

```jsx
// Single date
<div className="grid gap-1.5">
  <Label htmlFor="dob">Date of birth</Label>
  <DatePicker
    value={date}
    onChange={setDate}
    placeholder="Select your birthday"
  />
</div>
```

```jsx
// Date range
<DateRangePicker
  value={range}
  onChange={setRange}
  numberOfMonths={2}
/>
```

```jsx
// With presets — pair with Button shortcuts
<div className="flex items-center gap-2">
  <DatePicker value={date} onChange={setDate} />
  <Button variant="outline" size="sm" onClick={() => setDate(new Date())}>Today</Button>
</div>
```

Built internally from Popover + Button + Calendar. If you need a custom trigger or different popover positioning, compose the primitives directly — Calendar and Popover are exported from the same barrel.