Forms

DatePicker

A labeled date field. With a mouse or trackpad it is a text box you can type the date into (in the locale's numbers, or ISO; a two-digit year is the one within 50 years of today) with a button that opens a Calendar in a popover; on a touch screen it is the phone's own date picker (<input type="date">). Either way the value is an ISO date ("2026-10-11") and the form submits it under name, as a native date input does.

Type it, or choose it in the calendar.

Install

Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):

npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13

Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):

npx shadcn@latest add ourostack/ouro-ui/date-picker#v1.2.13

Or, with the @ouro namespace set up:

npx shadcn@latest add @ouro/ouro-base @ouro/date-picker

Its tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):

npx shadcn@latest add ourostack/ouro-ui/date-picker#v1.2.13 ourostack/ouro-ui/date-picker-tests#v1.2.13
import { DatePicker } from "@/components/ouro/calendar/date-picker";

Paths use shadcn's default @/ alias; the CLI rewrites them to yours.

Example

<DatePicker label="Delivery date" name="delivery" min="2026-10-11" required error={errors?.delivery} />
<DatePicker label="Birthday" value={birthday} onValueChange={setBirthday} locale="en-GB" />

Use instead when

  • Use Calendar instead for a calendar always on the page or for a range of dates, and TextField type="time" for a time.

Props

Type: DatePickerProps.

chooseDateLabel
Type: string
Default: "Choose date"
The name of the button that opens the calendar, as visually hidden text and not an aria-label, so a test's getByLabel("Date") finds the text box alone. Translate it with your app.
className
Type: string
Layout only: margins, width, grid placement (mt-4, col-span-2). Appearance classes warn in development; style fields in your theme.
closeLabel
Type: string
Default: "Close"
The label of the calendar's close button, which only screen readers and the keyboard reach. Translate it with your app.
defaultValue
Type: string
The date at first, as an ISO date, when you do not control value.
description
Type: ReactNode
Supporting text under the label, read after the label through aria-describedby.
disabled
Type: boolean
Disables the field and its calendar button, and fades it.
error
Type: ReadonlyErrorMessages
What is wrong, from your server action or client check. The message (or several, as a list) shows under the field, which gets aria-invalid. A message is text or a React node (one with a link); an empty string or array means valid.
hideLabel
Type: boolean
Hides the label visually; screen readers still read it as the field's name.
invalidDateMessage
Type: string
Default: "Enter a valid date."
The browser's message for typed text that is not a date, or a date isDateDisabled names. Translate it with your app.
isDateDisabled
Type: ((date: string) => boolean)
Marks single days unavailable: isDateDisabled={(date) => booked.has(date)}. It gets each day as an ISO date. Such a day, typed or picked in the phone's own picker, is invalid.
label (required)
Type: string
What the date is for, shown above the field and read as its name. Required: every control needs a name. To hide it visually, keep it and add hideLabel.
locale
Type: string
Default: "en-US"
The BCP 47 locale for the calendar and the typed format: "en-GB" reads 11/10/2026 as 11 October and starts weeks on Monday. Pass your app's locale; the default is the same on the server and in the browser. Phones show their own picker in the phone's language.
max
Type: string
The latest date allowed, as an ISO date. Later days are unavailable in the calendar, and a typed one is invalid.
min
Type: string
The earliest date allowed, as an ISO date. Earlier days are unavailable in the calendar, and a typed one is invalid.
name
Type: string
The form field name. The form submits the ISO date (2026-10-11) under it, or an empty string.
nextMonthLabel
Type: string
Default: "Next month"
The calendar's next month button's label. Translate it with your app.
onValueChange
Type: ((value: string) => void)
Called with the ISO date when the user picks or types an allowed date, and with "" when the field no longer holds one. A date before min, after max or named by isDateDisabled, typed or chosen in the phone's own picker, is not a value: it reports "", and the field shows why and blocks the submit.
outOfRangeMessage
Type: string
Default: "Enter a date in the allowed range."
The browser's message for a typed date before min or after max. Translate it with your app.
placeholder
Type: string
An example of what to type, shown while the field is empty: "MM/DD/YYYY". It is not a label.
previousMonthLabel
Type: string
Default: "Previous month"
The calendar's previous month button's label. Translate it with your app.
readOnly
Type: boolean
The field shows its date but cannot be changed; its calendar button is disabled.
ref
Type: Ref<HTMLInputElement>
The input element.
required
Type: boolean
A date must be given: the field gets required and aria-required, and the label a marker.
value
Type: string
The date as an ISO date ("2026-10-11"), or "" for none. Pass it with onValueChange to control the field.

Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

  • dateFormat: Not a prop. The locale sets the format: <DatePicker locale="en-GB">.
  • errorMessage: Not a prop. Ouro UI uses error: <DatePicker error="Required.">.
  • format: Not a prop. The locale sets the format: <DatePicker locale="en-GB">.
  • helperText: Not a prop. Ouro UI uses description: <DatePicker description="...">.
  • invalid: Not a prop. error marks the field invalid: <DatePicker error="What is wrong">.
  • isDisabled: Not a prop. Ouro UI uses disabled: <DatePicker disabled>.
  • isInvalid: Not a prop. error marks the field invalid: <DatePicker error="What is wrong">.
  • isRequired: Not a prop. Ouro UI uses required: <DatePicker required>.
  • maxDate: Not a prop. Ouro UI uses max: <DatePicker max="2026-12-31">.
  • minDate: Not a prop. Ouro UI uses min: <DatePicker min="2026-10-11">.
  • onChange: Not a prop. Ouro UI uses onValueChange, which gets the ISO date: <DatePicker onValueChange={setDate}>.
  • selected: Not a prop. Ouro UI uses value: <DatePicker value={date} onValueChange={setDate}>.

className is for layout only

Pass margins, width and grid placement (mt-4, w-full, col-span-2).

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.

Alignment classes (text-center) warn too: set alignment on a parent element.

Types

ErrorMessage

One error message: text, or a React element for a message that carries a link or other markup.

type ErrorMessage = string | ReactElement;

ReadonlyErrorMessages

A read-only message view accepted by fields and Form; mutable input arrays remain valid.

type ReadonlyErrorMessages = ErrorMessage | readonly ErrorMessage[];