// preview in
saved for every page

Date range picker

A start and an end date in one field, typed or picked from a range calendar. Use it for stays, leave, reports and anything that lasts more than a day.

Built on React AriaPattern WAI-ARIA Date Picker DialogClient componentSource ↗
minimal · light · #3fa34d

Shown on your status page and in auto-replies.

disabled
readOnly
invalid

Installation

Set up Camaleo once (Installation), then import what you need. The value is a { start, end } object of @internationalized/date dates, re-exported from the library. Put the picker in a Field to give it a label.

import { DateRangePicker, CalendarDate, isWeekend, Field, FieldLabel } from "@camaleo/react";

Anatomy

Two date inputs joined by a separator, and a button that opens a RangeCalendar in a popup. The popup renders in a portal and keeps the skin.

12/21/2026–12/31/2026trigger
[data-slot="date-range-picker"] · [data-slot="date-picker-control"][data-slot="date-range-separator"] · [data-slot="date-picker-trigger"] · [data-slot="date-picker-popup"][data-slot="date-input"] · [data-slot="date-segment"]

Examples

Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.

minimal · light · #3fa34d

Shown on your status page and in auto-replies.

5 nights

Controlled, with the length

Change the dates. Counting the nights under the field saves people from doing it in their head.

Up to 14 days in a row.

Split it into two requests of 14 days or less.

Longest range

There is no prop for a maximum length, so check it in onChange and set invalid on the Field. The error says how to fix it.

November 24 to 26 is booked. A stay can't run across it.

Booked dates

isDateUnavailable crosses out the booked days, and a range can't run across them. Say which dates are taken in the description too.

Weekends don't count, but the sprint can run across them.

Across weekends

With allowsNonContiguousRanges, the range can span unavailable days, like weekends in a sprint, without counting them.

Custom separator

separator replaces the dash, here with a word. It is hidden from screen readers, which name the two inputs start and end date.

Closed on November 1. Disputes go to billing@northwind.com.

Read-only

readOnly shows a closed period that can still be focused and read. Say where to go if it is wrong.

Required in a form

Send it empty. startName and endName submit two ISO dates, and the error goes away once both are picked.

In context

Ranges filter, book and plan. These are whole pieces of UI you can copy, in the skin you picked.

Sales report

Data up to October 28, 2026.

Report filter with presetsCard · Field · DateRangePicker · Button

Harbor View Inn, Portland

$180 per night, taxes included

4 nights · $720

Hotel bookingCard · Field · DateRangePicker · NumberField · Button

Request time off

Only business days count against your balance.

Time-off requestCard · NativeSelect · Field · DateRangePicker · Textarea · Button

Guidelines

DoOne field for one range.

The calendar shows the whole span, and the end can't land before the start.

Don’tTwo separate pickers for one stay.

Neither calendar shows the span, and people pick the second date without seeing the first.

10 business days, back on Monday, January 25.

DoSay how long it is.

"10 business days, back on Monday" confirms the range in words.

Don’tLeave people counting.

Two dates alone make people count days, and weekends are easy to miss.

Props

DateRangePicker takes the props of the React Aria date range picker. These are the ones you use most:

PropTypeDefaultDescription
classNamestring—Class name for the root element.
styleCSSProperties—Inline style for the root element.
openboolean—Opens or closes the calendar popup, controlled. Pair it with onOpenChange.
disabledboolean—Disables both inputs and the calendar button.
readOnlybooleanfalseShows the range and keeps it focusable, but it can't be changed.
invalidboolean—Marks the picker invalid. Inside a Field, the field's invalid is used when this is not set.
requiredbooleanfalseMarks both dates as required for assistive tech.
localestring—The locale for the segment order, month names and first day of the week, like "en-US". Defaults to the browser's.
weekdayStyle"narrow" | "short" | "long""short"How the weekday names in the calendar are written. Defaults to "short".
separatorReactNode"–"What goes between the start and the end date. Hidden from screen readers. Defaults to "–".
triggerLabelstring—Accessible name of the calendar button. React Aria provides a translated default.
previousLabelstring—Accessible name of the previous month button. React Aria provides a translated default.
nextLabelstring—Accessible name of the next month button. React Aria provides a translated default.
calendarFooterReactNode—Content shown under the calendar grid, like a legend or a note.
valueDateRange | null—The current value (controlled).
defaultValueDateRange | null—The default value (uncontrolled).
onChange(value: DateRange | null) => void—Handler that is called when the value changes.
onOpenChange(isOpen: boolean) => void—Handler that is called when the overlay's open state changes.
minValueDateValue—The minimum allowed date that a user may select.
maxValueDateValue—The maximum allowed date that a user may select.
placeholderValueDateValue—A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today's date at midnight.
granularity"day" | "hour" | "minute" | "second"—Determines the smallest unit that is displayed in the date picker. By default, this is "day" for dates, and "minute" for times.
firstDayOfWeek"sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat"—The day that starts the week.
allowsNonContiguousRangesboolean—When combined with isDateUnavailable, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected.
isDateUnavailable(date: DateValue) => boolean—Callback that is called for each date of the calendar. If it returns true, then the date is unavailable.
startNamestring—The name of the start date input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname).
endNamestring—The name of the end date input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname).

Other React Aria props pass through, such as defaultOpen, shouldCloseOnSelect, hourCycle, hideTimeZone, pageBehavior and autoFocus. Unlike DatePicker, it doesn't take name from the Field: use startName and endName.

Styling hooks

Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the picker itself. The calendar in the popup reads the RangeCalendar tokens.

Attributes

data-slot"date-range-picker" · "date-picker-control" · "date-input" · "date-range-separator" · "date-picker-trigger" · "date-picker-popup"
data-openon the root while the calendar is open
data-invalidon the root, the control and the segments when invalid
data-disabledpresent when disabled
data-focus-withinon the root and the control while a segment has focus
data-placeholder · data-typeon a segment: still empty · its part (month, day, year, literal…)

Tokens it reads

Color

--cml-accent--cml-accent-fg--cml-border--cml-border-control--cml-danger--cml-fg--cml-fg-muted--cml-fg-secondary--cml-field-bg--cml-field-border-sides--cml-field-shadow--cml-focus-offset--cml-focus-ring--cml-focus-width--cml-popover-bg--cml-popover-border--cml-shadow-popover--cml-surface-raised

Shape & size

--cml-border-width--cml-field-border-width--cml-field-height--cml-field-padding--cml-font-size-field--cml-popover-border-width--cml-radius-control--cml-radius-control-sm--cml-radius-field--cml-radius-popover

Type & motion

--cml-duration-fast--cml-ease-standard--cml-font-field

This component only

--cml-corner-shape--cml-disabled-opacity--cml-item-highlight--cml-segment-focus-bg--cml-segment-focus-border--cml-segment-focus-fg--cml-segment-focus-shadow--cml-segment-radius
/* a wider gap around the separator, only in the report toolbar */
.report-filters [data-slot="date-range-separator"] {
  padding-inline: 0.5rem;
}

Accessibility

TabMoves through the start segments, the end segments, then the calendar button.
Arrow Up / Arrow DownIncreases or decreases the focused segment.
Arrow Left / Arrow RightMoves to the previous or next segment, across both dates.
0–9Types into the segment and moves on when it is complete.
Alt + Arrow DownOpens the calendar from a segment.
Enter (calendar)Starts the range on the focused day; a second Enter ends it and closes the calendar.
Arrow keys (calendar)Move between days and extend the range while one is started.
EscapeCloses the calendar and returns focus to the button.
Was this page helpful? · Edit this page on GitHub