Date Picker

A date input with a calendar popover — type a date or pick it from the month grid.

<DatePicker value={value} onChange={setValue} />

Usage

DatePicker is a single, form-ready control: a DD/MM/YYYY text input with a trailing calendar button that opens the month grid. Type a date (digits auto-format with slashes) or pick one in the calendar — the two stay in sync. It speaks the native date contract (YYYY-MM-DD), so it drops straight into forms that previously used <input type="date">.

import { DatePicker } from "@reva/ui";

It is fully controlled. Hold the value as a YYYY-MM-DD string ("" when empty) and update it from onChange:

const [value, setValue] = useState("");

<DatePicker value={value} onChange={setValue} />;

Pass max to cap the latest selectable day (a date of birth can't be in the future, for example) and placeholder to override the default DD/MM/YYYY hint. The month/year dropdown caption makes distant dates — like dates of birth — quick to reach.

Examples

In a form field

Compose with Field for a label, hint, and error slot shared with other controls — this is the pattern used for "Date of birth" and "Incorporated on" across the product.

We use this to verify your identity.

<Field>  <FieldLabel htmlFor="date-of-birth">Date of birth</FieldLabel>  <DatePicker    id="date-of-birth"    value={value}    onChange={setValue}    max={todayISO}  />  <FieldDescription>We use this to verify your identity.</FieldDescription></Field>

Set max to keep selection in range — here future days are disabled and future typed dates are not committed.

Quick presets

Because the value is just a controlled string, sibling controls can write to it. Here preset buttons set the date that the input and calendar both read from.

<VStack gap={2}>  <HStack gap={2}>    <Button onClick={() => setValue(isoFromToday(0))} size="sm" variant="secondary">      Today    </Button>    <Button onClick={() => setValue(isoFromToday(7))} size="sm" variant="secondary">      +1 week    </Button>    <Button onClick={() => setValue(isoFromToday(30))} size="sm" variant="secondary">      +1 month    </Button>  </HStack>  <DatePicker value={value} onChange={setValue} /></VStack>

Reading the selected value

onChange emits the ISO YYYY-MM-DD string, ready for summaries, mutation payloads, or other fields.

Selected value: 2026-03-01

<VStack gap={3}>  <DatePicker value={value} onChange={setValue} />  <Text color="muted-foreground" size="sm">    Selected value: {value || "—"}  </Text></VStack>

Date range

There is no range component — DatePicker is single-only. For a range, compose a Popover and a Calendar in mode="range", triggered by a Select-styled control that reads back the chosen span. See Calendar for mode, numberOfMonths, and selected / onSelect.

<Popover>  <PopoverTrigger asChild>    <button      className={cn(selectTriggerVariants({ variant: "default" }), "w-full justify-between")}      type="button"    >      <span>{formatRangeLabel(range)}</span>      <CaretDown className="size-4 shrink-0 text-fg-muted" />    </button>  </PopoverTrigger>  <PopoverContent align="start" className="w-auto overflow-x-auto p-0">    <Calendar      mode="range"      numberOfMonths={2}      selected={range}      onSelect={setRange}    />  </PopoverContent></Popover>

Reusing selectTriggerVariants keeps the trigger visually identical to a Select while the calendar drives the selection.

Props

DatePicker is the single-date control. Range selection is composed from Calendar and Popover.

PropTypeDefaultDescription
valuestringControlled value as a YYYY-MM-DD string ("" when empty). Required.
onChange(value: string) => voidFires with the new YYYY-MM-DD string, or "" when the field is cleared. Required.
placeholderstring"DD/MM/YYYY"Placeholder text for the empty input.
maxstringLatest selectable date, inclusive, as YYYY-MM-DD. Later days are disabled and later typed dates are not committed.
idstringForwarded to the input — pair with FieldLabel htmlFor for an accessible label.

On this page