Skip to content

Date Range Picker

Start and end date fields with a two-month calendar and optional presets like 'Last 7 days'.

Inputv0.1.1experimentalWCAG 2.2 AAView spec
Report period
import { DateRangePicker } from "@rdloom/react";

export default function DateRangePickerBasicExample() {
  return (
    <DateRangePicker className="w-80" label="Report period" />
  );
}

Installation

npx rdloom add date-range-picker

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs @internationalized/date, react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/date-range-picker.json

Works, but without upgrade tracking.

Usage

import { DateRangePicker } from "@rdloom/react";

<DateRangePicker className="w-80" label="Report period" />

With presets

Report period
import { DateRangePicker, defaultDateRangePresets } from "@rdloom/react";

export default function DateRangePickerWithPresetsExample() {
  return (
    <DateRangePicker className="w-80" label="Report period" presets={defaultDateRangePresets} />
  );
}

Time zone

Tokyo office hours
Presets like Today use Tokyo's date
import { DateRangePicker, defaultDateRangePresets } from "@rdloom/react";

export default function DateRangePickerTimeZoneExample() {
  return (
    <DateRangePicker
      className="w-80"
      label="Tokyo office hours"
      description="Presets like Today use Tokyo's date"
      presets={defaultDateRangePresets}
      timeZone="Asia/Tokyo"
    />
  );
}

Min max

Booking
Up to 60 days ahead
import { getLocalTimeZone, today } from "@internationalized/date";
import { DateRangePicker } from "@rdloom/react";

export default function DateRangePickerMinMaxExample() {
  const now = today(getLocalTimeZone());
  return (
    <DateRangePicker className="w-80" label="Booking" description="Up to 60 days ahead" minValue={now} maxValue={now.add({ days: 60 })} />
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
labelrequired

Visible label.

stringnone
description

Help text under the field.

stringnone
errorMessage

Shown when the range is invalid.

stringnone
size

Field height and font size.

"sm" | "md" | "lg""md"
presets

Quick ranges shown beside the calendar. Pass defaultDateRangePresets or your own.

DateRangePreset[]none
timeZone

IANA time zone that presets use for 'today'. Defaults to the user's local zone.

stringnone
visibleMonths

Months shown in the popover.

number2
value

Controlled range.

DateRange | nullnone
defaultValue

Initial range when uncontrolled.

DateRangenone
minValue

Earliest allowed date.

DateValuenone
maxValue

Latest allowed date.

DateValuenone
isDisabled

Prevents editing and dims the field.

booleanfalse
isInvalid

Marks the range invalid and shows errorMessage.

booleanfalse
isRequired

Requires a range for form submission.

booleanfalse
onChange

Called when the range changes, including from a preset.

(value: DateRange | null) => voidnone

Accessibility

Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab moves between start and end segments
  • Up/Down arrows change the focused segment
  • Alt+Down opens the calendar, Esc closes it
  • Presets are buttons reachable with Tab

Screen readers announce

  • Start and end segments are distinguishable (start date / end date)
  • Presets are announced as buttons; the one matching the current range as pressed
  • Presets outside min/max are announced as unavailable
  • After choosing a preset, focus returns to the field and the new range can be read

What your code must do

  • Start and end fields are labelled as such
  • Choosing a preset announces the new range and returns focus to the field
  • Presets outside minValue/maxValue are disabled

Guidelines

Use it when

  • Filtering reports or analytics by period
  • Booking a stay

Avoid it when

  • A single date: use DatePicker

Don't

  • Hiding presets on mobile
  • Presets that ignore the user's time zone
  • Passing isInvalid for required or format checks (even isInvalid={false}): any defined value takes over validity and hides the built-in errors. Use isRequired and validate; pass isInvalid only for errors the app finds itself, such as from the server

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-surface-raised
  • --rd-color-surface-subtle
  • --rd-color-border-default
  • --rd-color-border-strong
  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-color-action-primary
  • --rd-color-feedback-danger
  • --rd-color-focus-ring
  • --rd-radius-control
  • --rd-radius-overlay