Date Range Picker
Date range pickers collect a start and end date in one control, such as bookings, reporting periods, and availability filters.
Try it
Usage
Compose the two inputs, separator, calendar trigger, popup, and calendar as children of RuiDateRangePicker. When no children are supplied, RuiDateRangePicker renders this same composition for you. Pass value as start/end ISO dates and give each input its own accessible name.
The default composition accepts startLabel and endLabel view props for the two native inputs. Use those props when the default structure is sufficient; set aria-label directly on RuiDateRangePickerStartInput and RuiDateRangePickerEndInput when composing the children yourself.
import {
RuiDateRangePicker,
RuiDateRangePickerCalendar,
RuiDateRangePickerControl,
RuiDateRangePickerEndInput,
RuiDateRangePickerInputs,
RuiDateRangePickerPopover,
RuiDateRangePickerSeparator,
RuiDateRangePickerStartInput,
RuiDateRangePickerToggle,
} from '@ecopages/radiant-ui/date-range-picker';
import { RuiField } from '@ecopages/radiant-ui/field';
import { RuiLabel } from '@ecopages/radiant-ui/label';
<RuiField name="tripDates">
<RuiLabel>Trip dates</RuiLabel>
<RuiDateRangePicker value="2026-08-01/2026-08-14" visibleMonths={2}>
<RuiDateRangePickerControl>
<RuiDateRangePickerInputs>
<RuiDateRangePickerStartInput aria-label="Start date" />
<RuiDateRangePickerSeparator />
<RuiDateRangePickerEndInput aria-label="End date" />
</RuiDateRangePickerInputs>
<RuiDateRangePickerToggle />
</RuiDateRangePickerControl>
<RuiDateRangePickerPopover>
<RuiDateRangePickerCalendar />
</RuiDateRangePickerPopover>
</RuiDateRangePicker>
</RuiField>Calendar input
Use the calendar button to select the start and end dates. After the first date is selected, the popup stays open for the second date and closes when the range is complete. Focus moves to the selected start date or the first available date when the popup opens.
Two-month view
Set visibleMonths to 2 so users can span month boundaries without extra navigation clicks.
Separate form names
Use startName and endName when the backend expects discrete fields instead of a combined range value.
Theming
Date Range Picker surfaces map to semantic surface roles, never Tailwind palette steps:
| Part | CSS roles |
|---|---|
Control group (rui-date-range-picker__group) | border, background, radius-control; focus-ring ring on focus-within |
| Input text | on-background, --text-control, --space-control-* |
| Placeholder | on-surface + opacity-muted |
| Separator | on-surface |
| Disabled | opacity-disabled |
Popup (rui-date-range-picker__popover) | popover surface + shadow-overlay (via rui-popover) |
Geometry shares control tokens (--size-control-*, --radius-control, --space-control-*) with RuiInput and RuiButton so form rows align. Override at the theme layer, not in component CSS.
API
RuiDateRangePicker is a custom element (<rui-date-range-picker>) that coordinates its light-DOM inputs, toggle button, and range calendar popover.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
value | string | '' | Canonical YYYY-MM-DD/YYYY-MM-DD range. |
min | string | '' | Earliest selectable ISO date. |
max | string | '' | Latest selectable ISO date. |
disabled | boolean | false | Disable both inputs and the calendar. |
read-only | boolean | false | Disable editing while keeping values visible. |
locale | string | '' | BCP 47 locale tag, or comma-separated fallback list. |
placeholder-start | string | Start date | Placeholder for the start input. |
placeholder-end | string | End date | Placeholder for the end input. |
start-name | string | '' | Native name for the start input. |
end-name | string | '' | Native name for the end input. |
name | string | '' | Reserved; use start-name / end-name for discrete fields. |
date-style | short · medium · long · full | medium | How committed values are displayed in the inputs. |
visible-months | number | 2 | Month grids shown in the range calendar popover. |
Events
| Event | Detail | Description |
|---|---|---|
rui-change | { value, start, end } | Fired when a valid range is committed (typing or calendar pick). |
JSX view props
These props configure the default child composition rendered by the RuiDateRangePicker JSX view. They are not custom-element attributes.
| Prop | Type | Default | Description |
|---|---|---|---|
startLabel | string | Start date | Accessible name for the default start-date input. |
endLabel | string | End date | Accessible name for the default end-date input. |
Composition helpers
Pass these helpers as RuiDateRangePicker children when the default structure is not enough:
| Helper | Renders |
|---|---|
RuiDateRangePickerControl | Bordered control row containing the inputs and trigger. |
RuiDateRangePickerInputs | Row containing the start input, separator, and end input. |
RuiDateRangePickerStartInput | Native text input for the range start. Give it an accessible name. |
RuiDateRangePickerSeparator | Visual separator between the two inputs. |
RuiDateRangePickerEndInput | Native text input for the range end. Give it an accessible name. |
RuiDateRangePickerToggle | Button that opens and closes the range calendar popup. |
RuiDateRangePickerPopover | Popup shell for the range calendar. |
RuiDateRangePickerCalendar | RuiCalendar in range-selection mode, synchronized with the picker value and constraints. |
CSS classes
Public BEM classes on the composed light-DOM surface (documented via @cssclass on RuiDateRangePicker):
| Class | Description |
|---|---|
.rui-date-range-picker | Root surface. |
.rui-date-range-picker__group | Bordered control-height row wrapping inputs and toggle. |
.rui-date-range-picker__values | Start / end input row. |
.rui-date-range-picker__input | A range text input. |
.rui-date-range-picker__separator | Em dash between the inputs. |
.rui-date-range-picker__popover | Range calendar popup shell (rui-popover / rui-floating). |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Control group | --border, --background, --radius-control, --focus-ring |
| Input text | --on-background, --text-control, --space-control-x, --space-control-y |
| Placeholder | --on-surface, --opacity-muted |
| Separator | --on-surface |
| Disabled | --opacity-disabled |
| Popup | --shadow-overlay (via rui-popover) |
Accessibility
- Give both native inputs distinct accessible names; placeholders alone are not sufficient. The example uses
aria-labelon each input. - The calendar trigger opens the popup and moves focus to the selected start date or first available date; arrow keys move between days, Page Up / Page Down change months, and Escape closes the popup.
- Surface validation errors for incomplete ranges before form submission.