HeroUI

RangeCalendarNew

Composable date range picker with month grid, navigation, and year picker support built on React Aria RangeCalendar

Import

import { RangeCalendar } from '@heroui/react';

Usage

Trip dates, February 2026

26
27
28
29
30
31
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
1
"use client";

import {RangeCalendar} from "@heroui/react";

export function Basic() {

Anatomy

import {RangeCalendar} from '@heroui/react';

export default () => (
  <RangeCalendar aria-label="Trip dates">
    <RangeCalendar.Header>
      <RangeCalendar.Heading />
      <RangeCalendar.NavButton slot="previous" />
      <RangeCalendar.NavButton slot="next" />
    </RangeCalendar.Header>
    <RangeCalendar.Grid>
      <RangeCalendar.GridHeader>
        {(day) => <RangeCalendar.HeaderCell>{day}</RangeCalendar.HeaderCell>}
      </RangeCalendar.GridHeader>
      <RangeCalendar.GridBody>
        {(date) => <RangeCalendar.Cell date={date} />}
      </RangeCalendar.GridBody>
    </RangeCalendar.Grid>
  </RangeCalendar>
)

Year Picker

RangeCalendar.YearPickerTrigger, RangeCalendar.YearPickerGrid, and their body/cell subcomponents provide an integrated year navigation pattern.

Trip dates, February 2026

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
"use client";

import {RangeCalendar} from "@heroui/react";

export function YearPicker() {

Default Value

Trip dates, February 2025

27
28
29
30
31
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
1
2
"use client";

import {RangeCalendar} from "@heroui/react";
import {parseDate} from "@internationalized/date";

Controlled

Trip dates, December 2025

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
1
2
3
4
Selected range: (none)
"use client";

import type {DateValue} from "@internationalized/date";

import {Button, ButtonGroup, Description, RangeCalendar} from "@heroui/react";

Min and Max Dates

Trip dates, February 2026

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
Select dates between today and 2026-05-19
"use client";

import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";

Unavailable Dates

Use isDateUnavailable to block dates such as weekends, holidays, or booked slots.

Trip dates, February 2026

26
27
28
29
30
31
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
1
Some days are unavailable
"use client";

import type {DateValue} from "@internationalized/date";

import {Description, RangeCalendar} from "@heroui/react";

Allows Non-Contiguous Ranges

Enable allowsNonContiguousRanges to allow selection across unavailable dates.

Trip dates, February 2026

26
27
28
29
30
31
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
1
Non-contiguous ranges are allowed across unavailable dates
"use client";

import type {DateValue} from "@internationalized/date";

import {Description, RangeCalendar} from "@heroui/react";

Disabled

Trip dates, February 2026

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
Range calendar is disabled
"use client";

import {Description, RangeCalendar} from "@heroui/react";

export function Disabled() {

Read Only

Trip dates, February 2026

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
Range calendar is read-only
"use client";

import {Description, RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, today} from "@internationalized/date";

Invalid

Trip dates, February 2026

26
27
28
29
30
31
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
1

Maximum stay duration is 1 week

"use client";

import type {DateValue} from "@internationalized/date";

import {Description, RangeCalendar} from "@heroui/react";

Focused Value

Trip dates, June 2025

26
27
28
29
30
31
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
1
2
3
4
5
6
Focused: 2025-06-15
"use client";

import type {DateValue} from "@internationalized/date";

import {Button, Description, RangeCalendar} from "@heroui/react";

Cell Indicators

You can customize RangeCalendar.Cell children and use RangeCalendar.CellIndicator to display metadata like events.

Trip dates, February 2026

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
"use client";

import {RangeCalendar} from "@heroui/react";
import {getLocalTimeZone, isToday} from "@internationalized/date";

Multiple Months

Render multiple grids with visibleDuration and offset for booking and planning experiences.

Trip dates, February to March 2026

February 2026
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
March 2026
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
1
2
3
4
"use client";

import {RangeCalendar} from "@heroui/react";
import {getLocalTimeZone} from "@internationalized/date";
import React from "react";

International Calendars

By default, RangeCalendar displays dates using the calendar system for the user's locale. You can override this by wrapping your RangeCalendar with I18nProvider and setting the Unicode calendar locale extension.

The example below shows the Indian calendar system:

Trip dates, शक 1947 माघ

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
1
2
"use client";

import {RangeCalendar} from "@heroui/react";
import {I18nProvider} from "react-aria-components";

Note: The onChange event always returns a date in the same calendar system as the value or defaultValue (Gregorian if no value is provided), regardless of the displayed locale.

Real-World Example

Booking range, February 2026

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
Blocked dates Weekend/Unavailable
"use client";

import type {DateValue} from "@internationalized/date";

import {Button, RangeCalendar} from "@heroui/react";

Styling

Passing Tailwind CSS classes

import {RangeCalendar} from '@heroui/react';

function CustomRangeCalendar() {
  return (
    <RangeCalendar aria-label="Trip dates" className="w-80 rounded-2xl border border-border bg-surface p-3 shadow-sm">
      <RangeCalendar.Header className="pb-3">
        <RangeCalendar.Heading className="text-default" />
        <RangeCalendar.NavButton slot="previous" className="text-default" />
        <RangeCalendar.NavButton slot="next" className="text-default" />
      </RangeCalendar.Header>
      <RangeCalendar.Grid>
        <RangeCalendar.GridHeader>
          {(day) => <RangeCalendar.HeaderCell>{day}</RangeCalendar.HeaderCell>}
        </RangeCalendar.GridHeader>
        <RangeCalendar.GridBody>
          {(date) => <RangeCalendar.Cell date={date} />}
        </RangeCalendar.GridBody>
      </RangeCalendar.Grid>
    </RangeCalendar>
  );
}

Customizing the component classes

@layer components {
  .range-calendar {
    @apply w-80 rounded-2xl border border-border bg-surface p-3 shadow-sm;
  }

  .range-calendar__heading {
    @apply text-sm font-semibold text-default;
  }

  .range-calendar__cell[data-selected="true"] .range-calendar__cell-button {
    @apply bg-accent text-accent-foreground;
  }
}

CSS Classes

RangeCalendar uses these classes in packages/styles/components/range-calendar.css and packages/styles/components/calendar-year-picker.css:

  • .range-calendar - Root container.
  • .range-calendar__header - Header row containing nav buttons and heading.
  • .range-calendar__heading - Current month label.
  • .range-calendar__nav-button - Previous/next navigation controls.
  • .range-calendar__grid - Main day grid.
  • .range-calendar__grid-header - Weekday header row wrapper.
  • .range-calendar__grid-body - Date rows wrapper.
  • .range-calendar__header-cell - Weekday header cell.
  • .range-calendar__cell - Interactive day cell wrapper.
  • .range-calendar__cell-button - Interactive day button inside each cell.
  • .range-calendar__cell-indicator - Dot indicator inside a day cell.
  • .calendar-year-picker__trigger - Year picker toggle button.
  • .calendar-year-picker__trigger-heading - Heading text inside year picker trigger.
  • .calendar-year-picker__trigger-indicator - Indicator icon inside year picker trigger.
  • .calendar-year-picker__year-grid - Overlay grid of selectable years.
  • .calendar-year-picker__year-cell - Individual year option.

Interactive States

RangeCalendar supports both pseudo-classes and React Aria data attributes:

  • Selected: [data-selected="true"]
  • Selection start: [data-selection-start="true"]
  • Selection end: [data-selection-end="true"]
  • Range middle: [data-selection-in-range="true"]
  • Today: [data-today="true"]
  • Unavailable: [data-unavailable="true"]
  • Outside month: [data-outside-month="true"]
  • Hovered: :hover or [data-hovered="true"]
  • Pressed: :active or [data-pressed="true"]
  • Focus visible: :focus-visible or [data-focus-visible="true"]
  • Disabled: :disabled or [data-disabled="true"]

API Reference

RangeCalendar Props

RangeCalendar inherits all props from React Aria RangeCalendar.

PropTypeDefaultDescription
valueRangeValue<DateValue> | null-Controlled selected range.
defaultValueRangeValue<DateValue> | null-Initial selected range (uncontrolled).
onChange(value: RangeValue<DateValue>) => void-Called when selection changes.
focusedValueDateValue-Controlled focused date.
onFocusChange(value: DateValue) => void-Called when focus moves to another date.
minValueDateValueCalendar-aware 1900-01-01Earliest selectable date.
maxValueDateValueCalendar-aware 2099-12-31Latest selectable date.
isDateUnavailable(date: DateValue) => boolean-Marks dates as unavailable.
allowsNonContiguousRangesbooleanfalseAllows ranges that span unavailable dates.
isDisabledbooleanfalseDisables interaction and selection.
isReadOnlybooleanfalseKeeps content readable but prevents selection changes.
isInvalidbooleanfalseMarks the calendar as invalid for validation UI.
visibleDuration{months?: number}{months: 1}Number of visible months.
defaultYearPickerOpenbooleanfalseInitial open state of internal year picker.
isYearPickerOpenboolean-Controlled year picker open state.
onYearPickerOpenChange(isOpen: boolean) => void-Called when year picker open state changes.

Composition Parts

ComponentDescription
RangeCalendar.HeaderHeader container for navigation and heading.
RangeCalendar.HeadingCurrent month/year heading.
RangeCalendar.NavButtonPrevious/next navigation control (slot="previous" or slot="next").
RangeCalendar.GridDay grid for one month (offset supported for multi-month layouts).
RangeCalendar.GridHeaderWeekday header container.
RangeCalendar.GridBodyDate cell body container.
RangeCalendar.HeaderCellWeekday label cell.
RangeCalendar.CellIndividual date cell.
RangeCalendar.CellIndicatorOptional indicator element for custom metadata.
RangeCalendar.YearPickerTriggerTrigger to toggle year-picker mode.
RangeCalendar.YearPickerTriggerHeadingLocalized heading content inside the year-picker trigger.
RangeCalendar.YearPickerTriggerIndicatorToggle icon inside the year-picker trigger.
RangeCalendar.YearPickerGridOverlay year selection grid container.
RangeCalendar.YearPickerGridBodyBody renderer for year grid cells.
RangeCalendar.YearPickerCellIndividual year option cell.

RangeCalendar.Cell Render Props

When RangeCalendar.Cell children is a function, React Aria render props are available:

PropTypeDescription
formattedDatestringLocalized day label for the cell.
isSelectedbooleanWhether the date is selected.
isSelectionStartbooleanWhether the date is the start of the selected range.
isSelectionEndbooleanWhether the date is the end of the selected range.
isUnavailablebooleanWhether the date is unavailable.
isDisabledbooleanWhether the cell is disabled.
isOutsideMonthbooleanWhether the date belongs to adjacent month.

For a complete list of supported calendar systems and their identifiers, see:

  • @internationalized/date — date types (CalendarDate, CalendarDateTime, ZonedDateTime) and utilities used by all date components
  • I18nProvider — override locale for a subtree
  • useLocale — read the current locale and layout direction

On this page