InputsReact
Two DateField inputs, a start and an end, with a calendar popup that picks the whole range.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<DateRangePicker.Root>
<DateRangePicker.Control>
<DateRangePicker.Input />
<DateRangePicker.Separator />
<DateRangePicker.Clear />
<DateRangePicker.Trigger />
</DateRangePicker.Control>
<DateRangePicker.Portal>
<DateRangePicker.Positioner>
<DateRangePicker.Popup>
<DateRangePicker.Calendar />
<DateRangePicker.Close />
</DateRangePicker.Popup>
</DateRangePicker.Positioner>
</DateRangePicker.Portal>
</DateRangePicker.Root>
// Not in this example: DateRangePicker.Preset, DateRangePicker.SegmentExamples
Presets
Presets for "Next 7 days" and "This month", resolved against today, beside the calendar.
Two Months
visibleMonths={2}: two months side by side, with a heading for each.
API reference
61 props across 13 parts, in React syntax. className, style, render are listed once, under Shared props.
CalendarDateRangePicker.Calendar
The calendar of the popup: Calendar.Root (or RangeCalendar.Root) wired to the picker's value, limits, locale and visible month. Compose it with the Calendar parts (Calendar.PrevButton, Calendar.Heading, Calendar.Grid and the rest). It takes no part in the Field: the control already submits and validates the value. Renders a <div>.
ClearDateRangePicker.Clear
Clears both inputs, including a partly typed one. It is out of the tab order: Backspace clears a segment from the keyboard. Renders a <button>.
CloseDateRangePicker.Close
A button that closes the popover. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
ControlDateRangePicker.Control
The group that holds the two inputs, the separator and the trigger, and the element the popup is anchored to. It does the Field's work for the range: it registers the value, marks the Field filled and dirty, validates on blur, and counts the popup as inside, so moving focus into the calendar neither touches nor validates the field. Renders a <div>.
InputDateRangePicker.Input
One of the two inputs of the range: a group named "Start date" or "End date" that holds the segments. An end typed before the start is not swapped: the end input is invalid and the Field shows "End date is before start date". Renders a <div>.
| Prop | Type | Default |
|---|---|---|
childrenThe segments, or a function of them: ({ segments }) => .... | React.ReactNode | ((context: { segments: FieldSegment[]; }) => React.ReactNode) | — |
typeWhich end of the range the input edits. | "start" | "end" | 'start' |
PopupDateRangePicker.Popup
The dialog that holds the calendar and the presets. It is named "Choose date" unless you name it. Focus moves to the highlighted day when it opens, and returns to the trigger (or to the segment Alt+Down was pressed on) when it closes. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the popover is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the popover is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
PortalDateRangePicker.Portal
A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
PositionerDateRangePicker.Positioner
Positions the popup against the control (the field and the trigger together). Renders a <div>.
| Prop | Type | Default |
|---|---|---|
alignHow to align the popup relative to the specified side. | Align | 'start' |
anchorAn element to position the popup against. | Element | VirtualElement | React.RefObject<Element | null> | (() => Element | VirtualElement | null) | null | — |
positionMethodDetermines which CSS position property to use. | "absolute" | "fixed" | 'absolute' |
sideWhich side of the anchor element to align the popup against. | Side | 'bottom' |
sideOffsetDistance between the anchor and the popup in pixels. | number | OffsetFunction | 0 |
alignOffsetAdditional offset along the alignment axis in pixels. | number | OffsetFunction | 0 |
collisionBoundaryAn element or a rectangle that delimits the area that the popup is confined to. | Boundary | 'clipping-ancestors' |
collisionPaddingAdditional space to maintain from the edge of the collision boundary. | Padding | 5 |
stickyWhether to maintain the popup in the viewport after | boolean | false |
arrowPaddingMinimum distance to maintain between the arrow and the edges of the popup. | number | 5 |
disableAnchorTrackingWhether to disable the popup from tracking any layout shift of its positioning anchor. | boolean | false |
collisionAvoidanceDetermines how to handle collisions when positioning the popup. | CollisionAvoidance | — |
PresetDateRangePicker.Preset
A button that sets the value to a date or a range, such as "Last 7 days". The value can be a function of today, which the picker reads from the clock after mount (or from today). It closes the popup like a day does. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
valuerequiredThe value the preset sets: a date ('2026-11-07'), a range ({ start, end }), or a function | `${number}-${number}-${number}` | DateRange | ((today: `${number}-${number}-${number}`) => `${number}-${number}-${number}` | DateRange) | — |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
RootDateRangePicker.Root
Groups all parts of a pair of fields with a calendar popup that picks a range of dates. It renders no element. Values are { start, end } with ISO 8601 strings; onValueChange is called only with a complete, ordered range, or null.
| Prop | Type | Default |
|---|---|---|
openWhether the popup is open. | boolean | — |
defaultOpenWhether the popup is initially open. | boolean | false |
onOpenChangeEvent handler called when the popup opens or closes. | ((open: boolean, eventDetails: DatePickerRootOpenChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the popup opens or closes. | ((open: boolean) => void) | — |
actionsRefA ref to imperative actions: unmount and close. | React.RefObject<PopoverRootActions | null> | — |
modalWhether the rest of the page is inert while the popup is open. | boolean | "trap-focus" | false |
closeOnSelectWhether choosing a day (the second end of a range) or a preset closes the popup. | boolean | true |
minThe earliest date that can be chosen. | `${number}-${number}-${number}` | — |
maxThe latest date that can be chosen. | `${number}-${number}-${number}` | — |
isDateUnavailableWhether a date cannot be chosen but can hold focus. | ((date: `${number}-${number}-${number}`, anchor: `${number}-${number}-${number}` | null) => boolean) | — |
isDateDisabledWhether a date is skipped by navigation and cannot be chosen. | ((date: `${number}-${number}-${number}`) => boolean) | — |
localeThe BCP 47 locale of the field and the calendar. | string | 'en-US' |
firstDayOfWeekThe first day of the week, 0 (Sunday) to 6. | number | — |
visibleMonthThe first visible month. | `${number}-${number}-${number}` | `${number}-${number}` | null | — |
onVisibleMonthChangeEvent handler called when the visible month changes. | ((month: `${number}-${number}`, eventDetails: DatePickerVisibleMonthChangeEventDetails) => void) | — |
visibleMonthsHow many months are visible. | number | 1 |
pageBehaviorWhether Previous and Next move by the visible months or by one. | "visible" | "single" | 'visible' |
fixedWeeksWhether every month shows six weeks. | boolean | false |
todayThe date of today, for the calendar and the presets. | `${number}-${number}-${number}` | — |
timeZoneThe time zone that decides what today is, when today is not given. | string | — |
labelsThe texts the picker, the field and the calendar add (the trigger and popup names, the Close | Partial<DatePickerLabels & DateFieldLabels & CalendarLabels> | — |
forceLeadingZerosWhether the day, month and hour show a leading zero. | boolean | — |
placeholderValueThe value an empty segment takes on its first Up or Down press. | string | — |
autoAdvanceWhether focus moves to the next segment when the typed number cannot get another digit. | boolean | true |
segmentRoleThe role of the segments. | DateFieldSegmentRole | 'spinbutton' |
onIncompleteChangeEvent handler called when the segments change while no complete value exists. | ((parts: FieldParts, eventDetails: any) => void) | — |
disabledWhether the component should ignore user interaction. | boolean | false |
readOnlyWhether the user should be unable to change the value. | boolean | false |
requiredWhether the user must fill in the field before submitting a form. | boolean | false |
invalidWhether the field is invalid. | boolean | false |
nameIdentifies the field when a form is submitted. | string | — |
formIdentifies the form that owns the hidden inputs. | string | — |
inputRefA ref to access the first segment. | React.Ref<HTMLInputElement> | — |
children | React.ReactNode | — |
valueThe range, null for none. | DateRange | null | — |
defaultValueThe range when initially rendered. | DateRange | null | — |
onValueChangeEvent handler called when the range changes: a complete, ordered range, or null when a | ((value: DateRange | null, eventDetails: DatePickerRootChangeEventDetails) => void) | — |
SegmentDateRangePicker.Segment
One segment of the value. An editable segment (year, month, day, hour, minute, second, millisecond, day period) is a real <input> with the spinbutton role; a literal (/, :) is a <span> hidden from assistive technology., or a <span> for a literal. Renders an <input>.
| Prop | Type | Default |
|---|---|---|
segmentrequiredThe segment to render, one of the segments the Input gives its children. | FieldSegment | — |
SeparatorDateRangePicker.Separator
The mark between the two inputs ("–"). It is hidden from assistive technology. Renders a <span>.
TriggerDateRangePicker.Trigger
A button that opens the popup. It is named "Choose date", or "Change date, Saturday, November 7, 2026" once a date is chosen. It sits inside the control. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
idID of the trigger. | string | — |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| AltArrowDown | On a segment of either input: opens the popup; Escape returns focus to that segment | Segment |
| EnterSpace | On a day: the first press sets the start, the second commits the range and closes | Popup |
| Escape | With a pending start: cancels it and keeps the popup open. Then: closes the popup | Popup |
| BackspaceDelete | In a segment of a committed range: clears it, and the value becomes null | Segment |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Control | role="group" named by the Field label; the Field's validity | the pair is announced as one control |
| Input | role="group" named "Start date" or "End date" (from labels) | each end is found and named on its own |
| Input | the end input is aria-invalid on its segments when it is before the start | the error is on the element that has focus |
| Trigger | aria-haspopup="dialog", aria-expanded; aria-label "Choose dates" or "Change dates, <start> to <end>" | the button says what the range is now |
| Popup | role="dialog" named "Choose dates"; the calendar announces the pending start in its status region | the first press is confirmed and the second end is asked for |
| Separator | aria-hidden | the dash is not read |
Focus order
- The segments of the start input, then those of the end input, in the locale's order; Clear is out of the order; then the Trigger.
- Opening moves focus to the start of the range (else today) in the calendar.
- Closing returns focus to the trigger, or to the segment the popup was opened from.
- Clear empties both inputs and focuses the first start segment.