DatePicker

Selects a date or date range from a calendar.

import { DatePicker } from "@usefragments/ui";Source

Preview

<DatePicker label="Start date" placeholder="Pick a date">
  <DatePicker.Trigger />
  <DatePicker.Content>
    <DatePicker.Calendar />
  </DatePicker.Content>
</DatePicker>

Usage

Use when

  • A date the user picks off a calendar
  • A start and end date together
  • Filtering by date

Avoid when

  • Time on its own (use a dedicated TimePicker)
  • A handful of known dates (use Select)
  • Typed date entry (use Input with a date mask)

Implementation notes

  • Pair range mode with numberOfMonths={2} so both ends are visible
  • size="sm" lines the trigger up with other dense toolbar controls
  • Offer at most a few presets for the common ranges: Today, Last 7 days, Last 30 days
  • disabledDates blocks past or unavailable days before the user tries
  • Placeholder names the date, not the action
  • A single pick closes the calendar at once; a range stays open until Escape or an outside click
  • DatePicker.Trigger defaults to type="button", so it is safe inside a form

Installation

pnpm add @usefragments/ui

Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Additional packages

  • react-day-picker — Optional — renders the calendar popup; the trigger and form field work without it.
pnpm add react-day-picker

Anatomy

Import DatePicker once, then compose its parts with dot notation.

  • DatePicker.Trigger
  • DatePicker.Content
  • DatePicker.Calendar
  • DatePicker.Preset
  • DatePicker.preload

Trigger opens one month; today carries the strong line.

<DatePicker label="Start date" placeholder="Pick a date">
  <DatePicker.Trigger />
  <DatePicker.Content>
    <DatePicker.Calendar />
  </DatePicker.Content>
</DatePicker>

Examples

Controlled

One value and one callback.

const [date, setDate] = React.useState<Date | null>(null);

<DatePicker label="Start date" value={date} onValueChange={setDate}>
  <DatePicker.Trigger />
  <DatePicker.Content>
    <DatePicker.Calendar />

Small Trigger

Matches the height of other sm toolbar controls.

<DatePicker size="sm" label="Last scanned" placeholder="Any time">
  <DatePicker.Trigger />
  <DatePicker.Content>
    <DatePicker.Calendar />
  </DatePicker.Content>
</DatePicker>

Date Range

Two months side by side so both ends of the range are visible.

import type { DateRange } from "@usefragments/ui";

const [range, setRange] = React.useState<DateRange | null>(null);

<DatePicker
  mode="range"

Disabled Dates

Days before today are struck through and unclickable.

<DatePicker
  label="Ships on"
  placeholder="Select a future date"
  disabledDates={(date) => date < new Date(new Date().setHours(0, 0, 0, 0))}
>
  <DatePicker.Trigger />

With Presets

A few ghost buttons above the calendar skip the common ranges.

const today = new Date(2026, 9, 4);
const weekAgo = new Date(2026, 8, 28);

<DatePicker mode="range" label="Window" numberOfMonths={2}>
  <DatePicker.Trigger />
  <DatePicker.Content>

With Helper Text

Label above the trigger, supporting text below it.

<DatePicker
  label="Renewal date"
  placeholder="Pick a date"
  helperText="Billing restarts on this day each month."
>
  <DatePicker.Trigger />

Invalid

The danger edge on the trigger, and the reason below it with an icon.

<DatePicker
  label="Start date"
  placeholder="Pick a date"
  invalid
  errorMessage="Pick a start date before the end date"
>

Read Only

The value with a dashed edge; the calendar stays shut.

<DatePicker label="Start date" defaultValue={new Date(2026, 9, 3)} readOnly>
  <DatePicker.Trigger />
  <DatePicker.Content>
    <DatePicker.Calendar />
  </DatePicker.Content>
</DatePicker>

Disabled

Dimmed; the calendar will not open.

<DatePicker label="Start date" placeholder="Pick a date" disabled>
  <DatePicker.Trigger />
  <DatePicker.Content>
    <DatePicker.Calendar />
  </DatePicker.Content>
</DatePicker>

API reference

Component props
Prop
Type
Default
Description
Calendar.classNamestring | undefinedNot set—
Calendar.numberOfMonthsnumber | undefinedNot setOverride number of months from root
childrenRequiredReact.ReactNodeNot setDatePicker trigger and content
classNamestring | undefinedNot setWrapper class name
Content.align"center" | "start" | "end" | undefinedcenterstartendNot set—
Content.childrenRequiredReact.ReactNodeNot set—
Content.sideOffsetnumber | undefinedNot set—
defaultValueDate | DateRange | null | undefinedNot setInitial value for uncontrolled use, typed by mode
disabledboolean | undefinedfalseDisable the picker
disabledDatesMatcher | Matcher[] | undefinedNot setDates to disable (react-day-picker Matcher | Matcher[])
errorMessagestring | undefinedNot setWords shown under the trigger while invalid, with a warning icon
format((date: Date) => string) | undefinedNot setFormats one date for the trigger. Defaults to Intl in the locale (long for single, medium for each range end); ranges join with an en dash
helperTextstring | undefinedNot setHelper text shown below the date picker
invalidboolean | undefinedfalseMarks the value invalid: the danger edge, aria-invalid and the error message
labelstring | undefinedNot setVisible label text above the date picker
localeLocale | undefinedNot setdate-fns locale for i18n
mode"range" | "single" | undefinedrangesinglesingleSelection mode
namestring | undefinedNot setHidden input name for forms
numberOfMonthsnumber | undefined1Number of months displayed side-by-side
onOpenChange((open: boolean) => void) | undefinedNot setPopover open state change callback
onValueChange((value: Date | null) => void) | ((value: DateRange | null) => void) | undefinedNot setCalled with the new value, typed by mode: (date: Date | null) in single mode, (range: DateRange | null) in range mode
openboolean | undefinedNot setControlled popover open state
placeholderstring | undefinedNot setTrigger placeholder text
Preset.childrenRequiredReact.ReactNodeNot set—
Preset.dateDate | undefinedNot setDate to select (single mode)
Preset.rangeDateRange | undefinedNot setRange to select (range mode)
readOnlyboolean | undefinedfalseShows the value with a dashed edge; the calendar stays shut
sizeDatePickerSize | undefinedsmmd"md"Trigger size
Trigger.childrenReact.ReactNodeNot set—
Trigger.placeholderstring | undefinedNot set—
valueDate | DateRange | null | undefinedNot setControlled value: Date | null in single mode, DateRange | null in range mode

Accessibility

  • The visible label names the trigger, followed by the current value (aria-labelledby)
  • Helper and error text describe the trigger (aria-describedby); invalid sets aria-invalid
  • The calendar grid is fully keyboard navigable; arrow keys move between days
  • Escape closes the calendar and focus returns to the trigger
  • Read-only keeps the trigger focusable but holds the calendar shut
  • SelectalternativeUse Select for choosing from a list of options
  • InputsiblingUse Input for free-form text date entry
  • PopovercompositionDatePicker uses Popover for the calendar dropdown

Next steps