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.
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-monoreponpx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13Then 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.13Or, with the @ouro namespace set up:
npx shadcn@latest add @ouro/ouro-base @ouro/date-pickerIts 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.13import { 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
Calendarinstead for a calendar always on the page or for a range of dates, andTextField 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'sgetByLabel("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
isDateDisablednames. 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 beforemin, aftermaxor named byisDateDisabled, 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
minor aftermax. 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
requiredandaria-required, and the label a marker. value- Type:
string - The date as an ISO date (
"2026-10-11"), or""for none. Pass it withonValueChangeto 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 useserror:<DatePicker error="Required.">.format: Not a prop. The locale sets the format:<DatePicker locale="en-GB">.helperText: Not a prop. Ouro UI usesdescription:<DatePicker description="...">.invalid: Not a prop.errormarks the field invalid:<DatePicker error="What is wrong">.isDisabled: Not a prop. Ouro UI usesdisabled:<DatePicker disabled>.isInvalid: Not a prop.errormarks the field invalid:<DatePicker error="What is wrong">.isRequired: Not a prop. Ouro UI usesrequired:<DatePicker required>.maxDate: Not a prop. Ouro UI usesmax:<DatePicker max="2026-12-31">.minDate: Not a prop. Ouro UI usesmin:<DatePicker min="2026-10-11">.onChange: Not a prop. Ouro UI usesonValueChange, which gets the ISO date:<DatePicker onValueChange={setDate}>.selected: Not a prop. Ouro UI usesvalue:<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[];