1. Components
  2. Dates
  3. Range Calendar

Range Calendar

A component that allows users to select a range of dates.

November 2024

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
<RangeCalendar />

Installation

npx dotui-cli@latest add range-calendar

Usage

Use RangeCalendar to allow users to select a contiguous or non-contiguous range of dates.

Options

Label

An aria-label must be provided to the RangeCalendar for accessibility. If it is labeled by a separate element, an aria-labelledby prop must be provided using the id of the labeling element instead.

Event date, November 2024

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
<RangeCalendar aria-label="Event date" />

Error message

RangeCalendar tries to avoid allowing the user to select invalid dates in the first place (see Min and max values and Unavailable dates). However, if according to application logic a selected date is invalid, Use isInvalid and errorMessage props.

Trip dates, November 2024

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
Maximum stay duration is 1 week
const [range, setRange] = React.useState({
  start: today(getLocalTimeZone()),
  end: today(getLocalTimeZone()).add({ weeks: 1, days: 3 }),
});
const isInvalid = range.end.compare(range.start) > 7;
return (
  <RangeCalendar
    value={range}
    onChange={setRange}
    isInvalid={isInvalid}
    errorMessage={isInvalid ? "Maximum stay duration is 1 week" : undefined}
  />
);

Min and max values

By default, Calendar allows selecting any date. The minValue and maxValue props can also be used to prevent the user from selecting dates outside a certain range.

Appointment date, November 2024

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
<RangeCalendar minValue={today(getLocalTimeZone())} />

Unavailable dates

Calendar supports marking certain dates as unavailable. These dates cannot be selected by the user and are displayed with a crossed out appearance. The isDateUnavailable prop accepts a callback that is called to evaluate whether each visible date is unavailable.

Trip dates, November 2024

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
const now = today(getLocalTimeZone());
const disabledRanges = [
  [now, now.add({ days: 5 })],
  [now.add({ days: 14 }), now.add({ days: 16 })],
  [now.add({ days: 23 }), now.add({ days: 24 })],
];

const isDateUnavailable = (date: DateValue) =>
  disabledRanges.some(
    (interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0
  );

return (
  <RangeCalendar
    minValue={today(getLocalTimeZone())}
    isDateUnavailable={isDateUnavailable}
  />
);

Non-contiguous ranges

The allowsNonContiguousRanges prop enables a range to be selected even if there are unavailable dates in the middle.

Trip dates, November 2024

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
const now = today(getLocalTimeZone());
const disabledRanges = [
  [now, now.add({ days: 5 })],
  [now.add({ days: 14 }), now.add({ days: 16 })],
  [now.add({ days: 23 }), now.add({ days: 24 })],
];
const isDateUnavailable = (date: DateValue) =>
  disabledRanges.some(
    (interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0
  );
return (
  <RangeCalendar
    minValue={today(getLocalTimeZone())}
    isDateUnavailable={isDateUnavailable}
    allowsNonContiguousRanges
  />
);

Visible months

By default, the RangeCalendar displays a single month. The visibleMonths prop allows displaying up to 3 months at a time.

November to December 2024

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
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
<RangeCalendar visibleMonths={2} />

Page behaviour

The pageBehavior prop allows you to control how the range-calendar navigates between months.

November to December 2024

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
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
<RangeCalendar visibleMonths={2} pageBehavior="single" />

Disabled

The isDisabled boolean prop makes the RangeCalendar disabled. cells cannot be focused or selected.

Appointment date, November 2024

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
<RangeCalendar isDisabled />

Read only

The isReadOnly boolean prop makes the RangeCalendar's value immutable. Unlike isDisabled, the RangeCalendar remains focusable.

Appointment date, November 2024

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
<RangeCalendar
  isReadOnly
  value={{
    start: today(getLocalTimeZone()),
    end: today(getLocalTimeZone()).add({ weeks: 1 }),
  }}
/>

Uncontrolled

An initial, uncontrolled value can be provided to the Calendar using the defaultValue prop.

Date (controlled), November 2024

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
<Calendar defaultValue={today(getLocalTimeZone())} />

Controlled

The Range Calendar component can be controlled by passing the value and onChange props.

Date (controlled), November 2024

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

Start date: 2024-11-22
End date: 2024-11-29

const [range, setRange] = React.useState<DateRange>({
  start: today(getLocalTimeZone()),
  end: today(getLocalTimeZone()).add({ weeks: 1 }),
});
return <RangeCalendar value={range} onChange={setRange} />

Composition

If you need to customize things further, you can drop down to the composition level.

November 2024

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
<RangeCalendarRoot>
  <CalendarHeader>
    <Button slot="previous" variant="outline" shape="square" size="sm">
      <ChevronLeftIcon />
    </Button>
    <Heading className="text-sm" />
    <Button slot="next" variant="outline" shape="square" size="sm">
      <ChevronRightIcon />
    </Button>
  </CalendarHeader>
  <CalendarGrid>
    <CalendarGridHeader>
      {(day) => <CalendarHeaderCell>{day}</CalendarHeaderCell>}
    </CalendarGridHeader>
    <CalendarGridBody>
      {(date) => (
        <CalendarCell date={date} range>
          {({ formattedDate }) => <span className="z-20">{formattedDate}</span>}
        </CalendarCell>
      )}
    </CalendarGridBody>
  </CalendarGrid>
</RangeCalendarRoot>

API Reference

PropTypeDefaultDescription
visibleMonths
number
1
The number of months to display at once. Up to 3 months are supported.
minValue
DateValue
-
The minimum allowed date that a user may select.
maxValue
DateValue
-
The maximum allowed date that a user may select.
isDateUnavailable
(date: DateValue) => boolean
-
Callback that is called for each date of the range calendar. If it returns true, then the date is unavailable.
isDisabled
boolean
false
Whether the range calendar is disabled.
isReadOnly
boolean
false
Whether the range calendar value is immutable.
autoFocus
boolean
false
Whether to automatically focus the range calendar when it mounts.
focusedValue
DateValue
-
Controls the currently focused date within the range calendar.
defaultFocusedValue
DateValue
-
The date that is focused when the range calendar first mounts (uncountrolled).
isInvalid
boolean
-
Whether the current selection is invalid according to application logic.
pageBehavior
'single' | 'visible'
'visible'
Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration.
value
RangeValue<DateValue> | null
-
The current value (controlled).
defaultValue
RangeValue<DateValue> | null
-
The default value (uncontrolled).
children
ReactNode | (values: RangeCalendarRenderProps & {defaultChildren: ReactNode | undefined}) => ReactNode
-
The children of the component. A function may be provided to alter the children based on component state.
className
string
-
The CSS className for the element.
style
CSSProperties | (values: RangeCalendarRenderProps & {defaultStyle: CSSProperties}) => CSSProperties
-
The inline style for the element. A function may be provided to compute the style based on component state.
EventTypeDescription
onFocusChange
(date: CalendarDate) => void
Handler that is called when the focused date changes.
onChange
(value: RangeValue<MappedDateValue<DateValue>>) => void
Handler that is called when the value changes.

Accessibility

Keyboard interactions

KeyDescription
Tab
Moves focus to the next focusable item in the range calendar.
Shift+Tab
Moves focus to the previous focusable item in the range calendar.
ArrowRight
Moves focus to the next day.
ArrowLeft
Moves focus to the previous day.
ArrowDown
Moves focus to the same day of the week in the next week.
ArrowUp
Moves focus to the same day of the week in the previous week.
Space Enter
Selects the focused date.

Last updated on 10/11/2024