RangeCalendar uses typed objects from [`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/) instead of native JavaScript `Date`. Import date utilities from `@cfa/react-core/dates` and components from `@cfa/react-core`. See the [Dates and Times guide](/guides/dates-and-times) for how to create values and convert to and from `Date`.

For single-date selection, use [Calendar](Calendar.md). For a field plus popover, use [DateRangePicker](DateRangePicker.md).

## Import

```jsx
import { RangeCalendar } from "@cfa/react-core";

import { CalendarDate } from "@cfa/react-core/dates";
```

### Basic Usage

The RangeCalendar component allows users to select a start and end date.

```jsx
<RangeCalendar
  defaultValue={{
    start: new CalendarDate(2025, 1, 5),
    end: new CalendarDate(2025, 1, 15),
  }}
/>
```

### Date Highlighting

Highlight specific dates in the calendar using the `isDateHighlighted` prop. This example highlights the 1st and 15th of each month.

```jsx
<RangeCalendar
  defaultValue={{
    start: new CalendarDate(2025, 1, 5),
    end: new CalendarDate(2025, 1, 15),
  }}
  isDateHighlighted={(date) => date.day === 1 || date.day === 15}
/>
```

### Date Restrictions

Set minimum and maximum dates to restrict selection, or disable specific dates.

```jsx
<RangeCalendar
  minValue={new CalendarDate(2025, 12, 15)}
  maxValue={new CalendarDate(2025, 12, 25)}
/>
```

### Multiple Months

Display multiple months at once using the `visibleDuration` prop.

```jsx
<RangeCalendar
  defaultValue={{
    start: new CalendarDate(2025, 1, 5),
    end: new CalendarDate(2025, 1, 15),
  }}
  visibleDuration={{ months: 2 }}
/>
```

### Non-Contiguous Ranges

Allow a selected range to skip unavailable dates with `allowsNonContiguousRanges`.

```jsx
<RangeCalendar
  defaultValue={{
    start: new CalendarDate(2025, 1, 5),
    end: new CalendarDate(2025, 1, 15),
  }}
  allowsNonContiguousRanges
  isDateUnavailable={(date) => date.day === 10 || date.day === 11}
/>
```

### Invalid

Show a validation message with `isInvalid` and `errorMessage`.

```jsx
<RangeCalendar
  isInvalid
  errorMessage="Selected range is invalid"
  defaultValue={{
    start: new CalendarDate(2025, 1, 5),
    end: new CalendarDate(2025, 1, 15),
  }}
/>
```

## Props

### RangeCalendar

| Name | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| errorMessage | `string` | - | - |
| hideOutsideDays | `boolean` | `false` | Hides days that belong to the previous or next month. The cells stay in the grid so week columns remain aligned. |
| isDateHighlighted | `((date: T) => boolean)` | - | - |
| allowsNonContiguousRanges | `boolean` | - | When combined with \`isDateUnavailable\`, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. |
| aria-describedby | `string` | - | Identifies the element (or elements) that describes the object. |
| aria-details | `string` | - | Identifies the element (or elements) that provide a detailed, extended description for the object. |
| aria-label | `string` | - | Defines a string value that labels the current element. |
| aria-labelledby | `string` | - | Identifies the element (or elements) that labels the current element. |
| autoFocus | `boolean` | `false` | Whether to automatically focus the calendar when it mounts. |
| children | `ChildrenOrFunction<RangeCalendarRenderProps>` | - | The children of the component. A function may be provided to alter the children based on component state. |
| className | `ClassNameOrFunction<RangeCalendarRenderProps>` | `'react-aria-RangeCalendar'` | The CSS \[className]\(https\://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. |
| commitBehavior | `"clear" \ | "reset" \ | "select"` | `'select'` | Controls the behavior when a pointer is released outside the calendar or a blur occurs mid selection:&#xA;&#xA;- \`clear\`: clear the currently selected range of dates.&#xA;- \`reset\`: reset the selection to the previously selected range of dates.&#xA;- \`select\`: select the currently hovered range of dates. |
| createCalendar | `((identifier: CalendarIdentifier) => Calendar)` | - | A function to create a new \[Calendar]\(https\://react-spectrum.adobe.com/internationalized/date/Calendar.html) object for a given calendar identifier. If not provided, the \`createCalendar\` function from \`@internationalized/date\` will be used. |
| defaultFocusedValue | `DateValue \ | null` | - | The date that is focused when the calendar first mounts (uncontrolled). |
| defaultValue | `RangeValue<T> \ | null` | - | The default value (uncontrolled). |
| dir | `string` | - | - |
| firstDayOfWeek | `"fri" \ | "mon" \ | "sat" \ | "sun" \ | "thu" \ | "tue" \ | "wed"` | - | The day that starts the week. |
| focusedValue | `DateValue \ | null` | - | Controls the currently focused date within the calendar. |
| hidden | `boolean` | - | - |
| id | `string` | - | The element's unique identifier. See \[MDN]\(https\://developer.mozilla.org/en-US/docs/Web/HTML/Global\_attributes/id). |
| inert | `boolean` | - | - |
| isDateUnavailable | `((date: DateValue, anchorDate: CalendarDate \ | null) => boolean)` | - | Callback that is called for each date of the calendar. If it returns true, then the date is unavailable. The second argument provides the current selection anchor date, if any. This can be used to adjust the available dates based on the user's first selected date. |
| isDisabled | `boolean` | `false` | Whether the calendar is disabled. |
| isInvalid | `boolean` | - | Whether the current selection is invalid according to application logic. |
| isReadOnly | `boolean` | `false` | Whether the calendar value is immutable. |
| lang | `string` | - | - |
| maxValue | `DateValue \ | null` | - | The maximum allowed date that a user may select. |
| minValue | `DateValue \ | null` | - | The minimum allowed date that a user may select. |
| onAnimationEnd | `AnimationEventHandler<HTMLDivElement>` | - | - |
| onAnimationEndCapture | `AnimationEventHandler<HTMLDivElement>` | - | - |
| onAnimationIteration | `AnimationEventHandler<HTMLDivElement>` | - | - |
| onAnimationIterationCapture | `AnimationEventHandler<HTMLDivElement>` | - | - |
| onAnimationStart | `AnimationEventHandler<HTMLDivElement>` | - | - |
| onAnimationStartCapture | `AnimationEventHandler<HTMLDivElement>` | - | - |
| onAuxClick | `MouseEventHandler<HTMLDivElement>` | - | - |
| onAuxClickCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onChange | `((value: RangeValue<MappedDateValue<T>>) => void)` | - | Handler that is called when the value changes. |
| onClick | `MouseEventHandler<HTMLDivElement>` | - | - |
| onClickCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onContextMenu | `MouseEventHandler<HTMLDivElement>` | - | - |
| onContextMenuCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onDoubleClick | `MouseEventHandler<HTMLDivElement>` | - | - |
| onDoubleClickCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onFocusChange | `((date: CalendarDate) => void)` | - | Handler that is called when the focused date changes. |
| onGotPointerCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onGotPointerCaptureCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onLostPointerCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onLostPointerCaptureCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onMouseDown | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseDownCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseEnter | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseLeave | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseMove | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseMoveCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseOut | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseOutCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseOver | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseOverCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseUp | `MouseEventHandler<HTMLDivElement>` | - | - |
| onMouseUpCapture | `MouseEventHandler<HTMLDivElement>` | - | - |
| onPointerCancel | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerCancelCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerDown | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerDownCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerEnter | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerLeave | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerMove | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerMoveCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerOut | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerOutCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerOver | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerOverCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerUp | `PointerEventHandler<HTMLDivElement>` | - | - |
| onPointerUpCapture | `PointerEventHandler<HTMLDivElement>` | - | - |
| onScroll | `UIEventHandler<HTMLDivElement>` | - | - |
| onScrollCapture | `UIEventHandler<HTMLDivElement>` | - | - |
| onTouchCancel | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchCancelCapture | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchEnd | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchEndCapture | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchMove | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchMoveCapture | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchStart | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTouchStartCapture | `TouchEventHandler<HTMLDivElement>` | - | - |
| onTransitionCancel | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionCancelCapture | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionEnd | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionEndCapture | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionRun | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionRunCapture | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionStart | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onTransitionStartCapture | `TransitionEventHandler<HTMLDivElement>` | - | - |
| onWheel | `WheelEventHandler<HTMLDivElement>` | - | - |
| onWheelCapture | `WheelEventHandler<HTMLDivElement>` | - | - |
| pageBehavior | `PageBehavior` | `visible` | Controls the behavior of paging. Pagination either works by advancing the visible page by visibleDuration (default) or one unit of visibleDuration. |
| render | `DOMRenderFunction<"div", RangeCalendarRenderProps>` | - | Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components.&#xA;&#xA;Requirements:&#xA;&#xA;- You must render the expected element type (e.g. if \`\<button>\` is expected, you cannot render an \`\<a>\`).&#xA;- Only a single root DOM element can be rendered (no fragments).&#xA;- You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate. |
| selectionAlignment | `"center" \ | "end" \ | "start"` | `'center'` | Determines the alignment of the visible months on initial render based on the current selection or current date if there is no selection. |
| slot | `string \ | null` | - | A slot name for the component. Slots allow the component to receive props from a parent component. An explicit \`null\` value indicates that the local props completely override all props received from a parent. |
| style | `StyleOrFunction<RangeCalendarRenderProps>` | - | The inline \[style]\(https\://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style) for the element. A function may be provided to compute the style based on component state. |
| suppressHydrationWarning | `boolean` | - | A React-only flag that suppresses the warning React normally logs when an element's server-rendered content doesn't match the client render during hydration. See \[React docs]\(https\://react.dev/reference/react-dom/client/hydrateRoot#suppressing-unavoidable-hydration-mismatch-errors). |
| translate | `"no" \ | "yes"` | - | - |
| value | `RangeValue<T> \ | null` | - | The current value (controlled). |
| visibleDuration | `DateDuration` | `{ months: 1 }` | The amount of days that will be displayed at once. This affects how pagination works. |
| weeksInMonth | `number` | - | The number of weeks in a month. This overrides the default set by the locale. |