DatePicker
A trigger + popover composition that lets users pick a single date or a date range through a locale-aware calendar — with Field integration, min/max constraints, and react-hook-form support.
Overview
<DatePicker> and <DatePicker.Range> are trigger + popover composites. The
trigger renders as a TextInput-styled button (border, padding, calendar icon) that
opens a <Popover> containing the existing <Calendar> component in single or
range mode. There is no typed text input in v1 — calendar selection is the only
input method; keyboard-typed date entry is deferred to a future release.
Display formatting uses Intl.DateTimeFormat with dateStyle: 'medium', so dates
render in the user's locale automatically (e.g. "May 21, 2026" in en-US, "21
mai 2026" in fr-FR). The locale comes from the nearest NoriProvider's i18n
context and can be overridden per instance via the locale prop.
Both components connect to <Field> via the standard Field.Control hook: id,
aria-labelledby, aria-describedby, aria-invalid, and aria-required are all
forwarded from the field envelope to the trigger automatically.
Basic usage
Range
DatePicker.Range takes a { start: CalendarDate | null; end: CalendarDate | null }
value. The popover closes automatically once both start and end are set.
Min / Max
Use minValue and maxValue to constrain the selectable range. Dates outside
the bounds are rendered as disabled in the calendar and cannot be selected.
Disabled
Set disabled to prevent any interaction. The trigger renders at 60% opacity
and aria-disabled is applied.
With Field
Wrap in <Field> for a label, description, and error message. The id,
aria-labelledby, aria-describedby, aria-required, and aria-invalid
attributes are injected automatically by Field.Control.
With an error message:
react-hook-form
Use <Controller> to wire DatePicker (or DatePicker.Range) into React Hook Form.
Single
Range
react-hook-form is not a peer dependency of @nori-ui/core. Install it
separately:
Accessibility
The trigger renders with role="combobox", aria-haspopup="dialog", and
aria-expanded (toggled as the popover opens/closes). When disabled, the
trigger also gets aria-disabled="true".
All aria-* props (aria-labelledby, aria-describedby, aria-invalid,
aria-required) are forwarded from the trigger props to the underlying
Pressable, so they are wired automatically when you use <Field>. You can
also pass them explicitly for custom label associations.
The calendar inside the popover follows the standard keyboard navigation
documented on the Calendar page. The Tab key
moves focus into and out of the popover; Escape closes it.
Locale
By default both components read the active locale from the nearest
NoriProvider's i18n context. This affects date formatting in the trigger and
the calendar's first day of week and weekday names.
To override per-instance, pass a BCP 47 locale tag:
The firstDayOfWeek prop lets you override the first day of week independently
of locale (e.g. force Monday in a locale that defaults to Sunday):
API reference
<DatePicker>
| Prop | Type | Default | Description |
|---|---|---|---|
aria-describedby | string | — | — |
aria-invalid | boolean | — | — |
aria-labelledby | string | — | — |
aria-required | boolean | — | — |
className | string | — | — |
defaultValue | CalendarDate | null | — | — |
disabled | boolean | false | Disable the whole picker. |
firstDayOfWeek | enum | — | First day of week override (0=Sun, 1=Mon, ...). Defaults from locale. |
id | string | — | — |
isDateUnavailable | (date: CalendarDate) => boolean | — | Custom unavailable predicate. |
locale | string | — | BCP 47 locale; defaults from NoriProvider's i18n context. |
maxValue | CalendarDate | — | — |
minValue | CalendarDate | — | Min/max selectable date. |
name | string | — | — |
onChange | (date: CalendarDate | null) => void | — | — |
placeholder | string | — | Placeholder text shown when no value. |
testID | string | — | — |
value | CalendarDate | null | — | — |
<DatePicker.Range>
Accepts the same props as <DatePicker> with the following differences:
value—{ start: CalendarDate | null; end: CalendarDate | null }.defaultValue— uncontrolled initial range; same shape asvalue.onChange— fires with the updated range object on each selection.
All other props (disabled, placeholder, locale, minValue, maxValue,
firstDayOfWeek, isDateUnavailable, id, aria-labelledby,
aria-describedby, aria-invalid, aria-required) behave identically to
<DatePicker>.