Skip to content

DatePickerInput

DatePickerInput is the standalone date and date-time picker. The user types into locale-ordered segments (day, month, year and, optionally, hour and minute) or picks a day from a calendar popover. Your code only sees a plain JavaScript Date | null; the React Aria calendar values the control uses internally never cross its props.

Use it for date entry in ordinary React state. To bind a Date property on an Arc command, use CalendarField, which wraps this control.

import { DatePickerInput } from '@cratis/components/Common';
import { useState } from 'react';
import { DatePickerInput } from '@cratis/components/Common';
export const SampleDeliveryDate = () => {
const [deliveryDate, setDeliveryDate] = useState<Date | null>(null);
const earliest = new Date(2030, 0, 1);
const latest = new Date(2030, 11, 31);
const outOfRange =
deliveryDate !== null && (deliveryDate < earliest || deliveryDate > latest);
return (
<div>
<DatePickerInput
id='sample-delivery-date'
aria-label='Delivery date'
aria-describedby='sample-delivery-date-help'
value={deliveryDate}
onChange={setDeliveryDate}
minDate={earliest}
maxDate={latest}
invalid={outOfRange}
placeholder='Choose a date'
showButtonBar
/>
<p id='sample-delivery-date-help'>
{outOfRange ? 'Pick a date in 2030.' : 'Deliveries run throughout 2030.'}
</p>
</div>
);
};

Render it under CratisComponentsProvider so the locale and labels come from your configuration. Without the provider, the labels use the English defaults and React Aria falls back to the browser’s locale. value and onChange are both required: the control is controlled, so pass the new date back through value.

minDate/maxDate disable out-of-range calendar cells and the Today action, but typing or stepping a segment can still produce an out-of-range date, and onChange receives it. The example therefore computes invalid from the value itself. See Validation and states.

AspectBehavior
Value typeDate | null. null means no date. Conversion to and from @internationalized/date happens inside the component.
Time zoneConversions use the browser’s local time zone.
Date-only mode (default)The time portion of an incoming value is ignored. Emitted dates are local midnight.
Date-time mode (showTime)Hour and minute segments are added. minDate/maxDate are compared including their time.
onChange signature(value: Date | null, meta?: ChangeMeta) => void. A React state setter can be passed directly.
meta.source'user' for every change the component emits.
meta.nativeEventPresent for the Today and Clear actions; absent for segment and calendar changes.
Segment editingonChange fires when the segments form a complete date. Clearing one segment leaves the last complete value in place without a callback; clearing every segment emits null.
Today actionEmits today’s date (local midnight, also in date-time mode). Disabled and inert when today falls outside minDate/maxDate, or the picker is disabled or read-only.
Clear actionEmits null. Disabled and inert when the picker is disabled or read-only, or when pt.clear.disabled is set.
Programmatic changesChanging value from outside never calls onChange.
Disabled or read-onlySegments cannot change the value. The trigger and Today/Clear actions are disabled; read-only segments remain focusable, but cannot open the calendar with Alt+ArrowDown/Alt+ArrowUp.

The Today and Clear buttons call onChange directly; they do not close the popover themselves.

PropTypeDefaultBehavior
valueDate | nullRequiredControlled value.
onChangeChangeHandler<Date | null>RequiredReceives the next date or null and optional change metadata.
onBlurFocusEventHandler<HTMLElement>—Attached to the root wrapper. React focus events bubble, so it also fires when focus moves between segments or into the calendar, not only when focus leaves the picker.
invalidbooleanfalseMarks the picker invalid. See Validation and states.
disabledbooleanfalseDisables the segments, trigger, calendar and Today/Clear actions.
readOnlybooleanfalseKeeps segments focusable but prevents editing; disables the trigger and Today/Clear actions and prevents opening the calendar with Alt+ArrowDown/Alt+ArrowUp.
idstring—DOM id of the segmented-input group.
placeholderstring—Text shown while the value is null and the field is not focused. Also used as the accessible name when aria-label is absent.
showIconbooleantrueRenders the calendar trigger button. Without it, the calendar is still reachable with Alt+ArrowDown.
showButtonBarbooleanfalseAdds Today and Clear actions below the calendar.
showTimebooleanfalseAdds hour and minute segments.
hourFormat'12' | '24'LocaleHour cycle for the time segments. Omit it to use the locale’s hour cycle.
minDate / maxDateDateUnboundedEarliest and latest selectable date for the calendar and the Today action.
todayLabel / clearLabelstringProvider messagePer-instance labels for the Today and Clear actions.
aria-labelstringSee LabelsAccessible name of the segmented-input group.
aria-labelledbystring—Id of an element that labels the picker.
aria-describedbystring—Id of an element that describes the picker.
classNamestring—Added to the root element, after pt.root.className.
styleCSSProperties—Inline style on the root element; merged over pt.root.style.
ptDatePickerInputPassThrough—Per-part attributes. See Stable parts.
dateFormatstring—Accepted for source compatibility and ignored. The locale controls formatting.
ptOptionsobject—Deprecated and ignored. Parts always merge.
unstyledboolean—Deprecated and ignored. Style through pt and CSS.

DatePickerInputProps and DatePickerInputPassThrough are exported types.

Top-level props take precedence over their pt.input equivalents. When a top-level prop is omitted, the component falls back to pt.input.id, disabled, readOnly, placeholder, aria-invalid, aria-label, aria-labelledby and aria-describedby; pt.input.id is applied to the group, like id. Other pt.input attributes, such as data-*, are forwarded to the segmented input.

The locale comes from the nearest CratisComponentsProvider (value.locale, default en-US; an invalid locale also falls back to en-US). It decides segment order, separators, the calendar system and the default hour cycle. There is no per-instance locale prop.

Components-owned text comes from messages.datePicker:

Message keyUsed forDefault
labelAccessible name fallback for the segmented inputDate
todayToday actionToday
clearClear actionClear
openCalendarCalendar trigger nameOpen calendar
previousMonthPrevious-month button namePrevious month
nextMonthNext-month button nameNext month
import { CratisComponentsProvider } from '@cratis/components';
<CratisComponentsProvider
value={{
locale: 'nb-NO',
messages: {
datePicker: {
label: 'Dato',
today: 'I dag',
clear: 'Tøm',
openCalendar: 'Åpne kalender',
previousMonth: 'Forrige måned',
nextMonth: 'Neste måned',
},
},
}}
>
<SampleDeliveryDate />
</CratisComponentsProvider>;

Resolution order for each text:

  • Accessible name: aria-label → pt.input['aria-label'] → placeholder → messages.datePicker.label → Date.
  • Today / Clear: todayLabel / clearLabel → messages.datePicker.today / .clear → English default.
  • Trigger and month buttons: pt.trigger, pt.previous or pt.next aria-label → provider message → English default.

The previous- and next-month glyphs follow the provider’s icons.previous and icons.next. See Localize owned labels and Register an icon set.

The segmented input, calendar and popover are React Aria’s DatePicker parts. The behavior below is what React Aria provides in the installed version; verify it with the assistive technologies your application supports.

KeyWhereEffect
Tab / Shift+TabSegments, triggerEach editable segment is its own tab stop, followed by the trigger.
ArrowLeft / ArrowRightSegmentMove to the previous or next segment (visual order in right-to-left locales).
ArrowUp / ArrowDownSegmentIncrement or decrement the segment, wrapping at its limits.
PageUp / PageDownSegmentStep by a larger amount (for example 7 days, 2 months, 5 years, 15 minutes).
Home / EndSegmentSet the segment to its minimum or maximum.
DigitsSegmentType the segment value.
Backspace / DeleteSegmentRemove the last digit, then clear the segment.
Alt+ArrowDown / Alt+ArrowUpSegmented inputOpen the calendar popover unless the picker is read-only.

Inside the popover, the calendar grid follows React Aria’s calendar keyboard model; see the React Aria DatePicker documentation.

What assistive technology receives:

  • The segmented input is a group carrying the resolved accessible name, aria-labelledby, aria-describedby and, when invalid is set, aria-invalid.
  • Each editable segment is a spinbutton named with its localized segment type followed by the group’s name, for example “month, Delivery date”. On iOS, where VoiceOver cannot focus spinbuttons, React Aria renders segments as textbox instead.
  • Separator segments (such as / or .) are hidden from assistive technology.
  • aria-describedby is applied to the first editable segment only, unless the field is invalid, so the description is not repeated on every segment.
  • The visible placeholder is aria-hidden; its text reaches screen readers as the accessible name fallback instead.
  • The trigger, previous-month and next-month buttons have the localized names listed above; their glyphs are aria-hidden.

Editable segments are at least 24 × 24 CSS pixels through the built-in stylesheet; separators are excluded. The SegmentHitTargets and NarrowShowTime12Hour stories assert that geometry in the browser test matrix. CSS that overrides .cratis-date-picker__segment or the segment part can undo it.

invalid is the only input to the Cratis invalid state. When it is true, the root, group, input, every segment and the trigger get data-invalid="true", the group gets aria-invalid="true", and the built-in stylesheet draws the group border in the error color. When it is false, no invalid attribute is rendered.

The component does not display an error message. Render one yourself and connect it with aria-describedby, as in the example.

Out-of-range values are not flagged for you. A date typed or stepped past minDate/maxDate reaches onChange and does not set data-invalid. Check the range in your own code and set invalid from the result.

disabled sets data-disabled on the root, group, input, segments, trigger and Today/Clear actions, and disables the trigger and actions. readOnly sets data-readonly on the root, group, input, segments and trigger and disables the trigger (which then also carries data-disabled) and Today/Clear actions (which also carry data-disabled), but leaves the root without data-disabled.

Each part below carries data-cratis-part with the name in the DOM part column. Style and query these names rather than internal class names or React Aria markup. The popover and its descendants are portaled into the provider’s overlay container, so query them from document, not from the picker’s container. See Choose an overlay container.

pt keyDOM partElementCanonical states
rootrootOuter wrapperdisabled, selected, open, invalid, readonly
groupgroupFocusable segmented-input group; also data-empty while value is nulldisabled, selected, open, invalid, readonly
inputinputSegmented date inputdisabled, selected, open, invalid, readonly
segmentsegmentOne date/time segment; data-type names it, for example day, month, year, hour, minute, dayPeriod or literaldisabled, invalid, readonly
placeholderplaceholderVisible empty-value textnone
triggertriggerCalendar trigger buttondisabled, selected, open, invalid, readonly
popoverpopoverPortaled calendar popoveropen
dialogdialogCalendar dialogopen
calendarcalendarCalendar rootopen
headerheaderMonth navigation rownone
previouspreviousPrevious-month buttonnone
headingheadingCurrent month and yearnone
nextnextNext-month buttonnone
gridgridCalendar gridnone
cellcellOne day cellselected
buttonBarbutton-barToday/Clear rownone
todaytodayToday actiondisabled
clearclearClear actiondisabled

The Cratis state attributes are present only while the state applies and are never rendered as "false". React Aria adds its own attributes on some parts, such as data-focused and data-placeholder on segments and data-disabled or data-outside-month on cells; the built-in stylesheet uses them, but they are not part of the Cratis state contract.

The pt key for the Today/Clear row is buttonBar, while its DOM part is button-bar. For the shared pt model, state vocabulary and cascade rules, see Pass-through and the Styling overview.

DatePickerInput renders through the display.datePicker slot. The bundled renderer adapters do not declare that slot, so the built-in implementation described here renders unless an adapter explicitly provides one. See Renderers.