RangeCalendar renders a header (heading or year-picker trigger, plus previous/next buttons) followed by a 7-column day grid, with an optional overlay year grid. Unlike Calendar, each day is a cell wrapping a cellButton — Reka puts the selection/today/outside-view state on the inner button, not the outer cell.
When selecting a range, is-date-unavailable receives a second argument, anchorDate, set to the first selected date. Use it to limit which end dates are valid (for example, within 7 days of the start).
Trip dates, October 2026
October 2026
Sun
Mon
Tue
Wed
Thu
Fri
Sat
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
After selecting a start date, only dates within 7 days are available
<script setup lang="ts">import type { DateValue } from '@internationalized/date'import { Description, RangeCalendar } from '@hareui/vue'import { getLocalTimeZone, today } from '@internationalized/date'const now = today(getLocalTimeZone())function isDateUnavailable(date: DateValue, anchorDate: DateValue | null) { return anchorDate != null && Math.abs(date.compare(anchorDate)) > 7}</script><template> <div class="flex flex-col items-center gap-4"> <RangeCalendar aria-label="Trip dates" :is-date-unavailable="isDateUnavailable" :min-value="now" /> <Description class="text-center"> After selecting a start date, only dates within 7 days are available </Description> </div></template>
Set visible-duration to { weeks: n } to show one or more weeks at a time. Navigation advances by the visible week range. Use page-behavior="single" to move one week at a time when showing multiple weeks.
Set visible-duration to { days: n } to show a rolling window of consecutive days. Navigation advances by the visible day range. Use page-behavior="single" to move one day at a time when showing multiple days.
Set number-of-months to show several months side by side. The previous/next buttons always page by the full visible width, matching React Aria's default behavior for multi-month calendars.
Note:update:modelValue always emits dates in the same calendar system as model-value or default-value (Gregorian if neither is given), regardless of the displayed locale.
Week and day views (visible-duration) are rendered by HareUI's own grid, since Reka's CalendarRoot/RangeCalendarRoot always build full month grids. They follow React Aria's visibleDuration behaviour (alignment, paging, page-behavior, keyboard navigation) and HeroUI's --week-view/--day-view styles; the year picker, number-of-months and fixed-weeks only apply to the month view.
Anchor-aware is-date-unavailable: Reka's matcher takes one argument, so RangeCalendar wraps the two-argument callback in a closure that reads the in-progress anchor (the picked start while end is unset, null once the range is complete, as in react-stately). It also narrows Reka's min-value/max-value to the run of available days around the anchor (react-stately's getAvailableRange), so paging past it is disabled, and marks the lone anchor cell as both selection start and end, as React Aria does.
fixed-weeks is boolean only (always 6 weeks). HeroUI's React weeksInMonth accepts any count up to 6; Reka's RangeCalendarRoot only exposes the all-or-nothing fixedWeeks flag.
invalid sets aria-invalid/data-invalid directly rather than through Reka: Reka's own isInvalid is a read-only computed derived from isDateUnavailable/minValue/maxValue, with no input prop to override it for arbitrary consumer validation (e.g. "stay too long").
No press-and-drag range selection: React Aria begins the live anchor on raw pointerdown (useCalendarState's anchorDate), letting a single press-move-release gesture preview and commit a range. Reka's RangeCalendarCellTrigger commits exclusively through its own click handler's changeDate state machine (RangeCalendarCellTrigger.vue:106-171), which is gated on startValue/lastPressedDateValue set by that same handler. Writing to Reka's internal startValue ref from an external pointerdown listener to emulate the anchor (tested against both a two-click gesture and a pointerdown+pointerenter+pointerup drag gesture) breaks the state machine rather than extending it: the click gesture commits nothing at all (changeDate takes the line 112-126 "already anchored" branch on the very first real click, since lastPressedDateValue doesn't match yet, so it returns without setting start or end), and the drag gesture commits only start (pointerup isn't one of Reka's commit triggers). There's no props-level override for the anchor/commit logic, so HeroUI's drag-select input method has no port; the two-click gesture (click a start day, hover to preview, click an end day) is fully supported and is what the demos above use.
Callback returning whether a given date is unavailable for selection. During an
in-progress range selection, anchorDate is the already-picked start date (or null
before any date is picked), matching React Aria's isDateUnavailable(date, anchorDate).
disabled
boolean
false
Whether the whole calendar is disabled.
readonly
boolean
false
Whether the calendar is read-only (dates stay visible but cannot be changed).
allowNonContiguousRanges
boolean
false
Whether non-contiguous ranges are allowed, skipping over unavailable dates in the middle
of the range instead of resetting the selection.
maximumDays
number
-
Restricts a range selection to a fixed number of days from the start date.
locale
string
-
The locale used for formatting and week calculations.
Whether to always display 6 weeks, avoiding layout shift between months.
Reka UI only supports a fixed 6-week grid (HeroUI's React weeksInMonth accepts
any count up to 6; see reka-limitations in the RangeCalendar report).
yearPicker
boolean
false
Replaces the plain month/year heading with a toggleable year grid.
indicator
((date: DateValue) => boolean)
-
Callback returning whether a small dot indicator should render under a date.
pagedNavigation
boolean
false
This property causes the previous/next buttons to navigate by the number of
months displayed at once rather than one month.
numberOfMonths
number
1
Shows this many months side by side. Navigating with the prev/next buttons always pages
by the full visible width (Reka's pagedNavigation, forced on regardless of the
pagedNavigation prop, matching React Aria's default pageBehavior: "visible").
Reka's heading only formats the combined range, so each panel renders its own
"<Month> <Year>" label derived from that panel's Grid.value (see reka-limitations in
the RangeCalendar report).
invalid
boolean
false
Marks the calendar as invalid for consumer-driven validation (e.g. a selected range that's
too long). Reka's own isInvalid is a read-only computed derived from isDateUnavailable/
minValue/maxValue, with no input prop to override it (see reka-limitations in the
RangeCalendar report), so this sets aria-invalid/data-invalid directly instead. HeroUI's
styles don't key off it either, so it has no visual effect by itself.
dir
"ltr" | "rtl"
-
The reading direction of the calendar.
visibleDuration
VisibleDuration
{ months: 1 }
The visible time range. { weeks: n } shows a week view and { days: n } a day view (a rolling window of
consecutive days); anything else shows the month view (use number-of-months for several months).
Navigation advances by the visible range.
pageBehavior
PageBehavior
"visible"
In week/day views, whether the prev/next buttons and page keys move by the whole visible range
('visible') or by one week/day ('single').