---
name: range-calendar
title: Range Calendar
url: https://ui.frontic.com/components/ui/range-calendar.md
tier: ui
install: npx @frontic/ui add range-calendar
files:
  - components/ui/range-calendar/RangeCalendar.vue
  - components/ui/range-calendar/RangeCalendarCell.vue
  - components/ui/range-calendar/RangeCalendarCellTrigger.vue
  - components/ui/range-calendar/RangeCalendarGrid.vue
  - components/ui/range-calendar/RangeCalendarGridBody.vue
  - components/ui/range-calendar/RangeCalendarGridHead.vue
  - components/ui/range-calendar/RangeCalendarGridRow.vue
  - components/ui/range-calendar/RangeCalendarHeadCell.vue
  - components/ui/range-calendar/RangeCalendarHeader.vue
  - components/ui/range-calendar/RangeCalendarHeading.vue
  - components/ui/range-calendar/RangeCalendarNextButton.vue
  - components/ui/range-calendar/RangeCalendarPrevButton.vue
  - components/ui/range-calendar/index.ts
dependencies:
  - "@internationalized/date"
  - "@lucide/vue"
  - "@vueuse/core"
  - class-variance-authority
  - reka-ui
---

# Range Calendar

Lets the shopper pick a start and an end date from one or more months, within a minimum and maximum and up to a set number of days.

## Install

```sh
npx @frontic/ui add range-calendar
```

Used by: [Date Range Picker](https://ui.frontic.com/components/ui/date-range-picker.md)

npm packages: `@internationalized/date`, `@lucide/vue`, `@vueuse/core`, `class-variance-authority`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { getLocalTimeZone, today } from '@internationalized/date'
import type { DateRange } from 'reka-ui'
import { shallowRef } from 'vue'
import { RangeCalendar } from '@/components/ui/range-calendar'

const start = today(getLocalTimeZone()).add({ days: 3 })
const stay = shallowRef<DateRange>({ start, end: start.add({ days: 4 }) })
</script>

<template>
  <RangeCalendar v-model="stay" :number-of-months="2" />
</template>
```

## Anatomy

```vue
<RangeCalendar>
  <RangeCalendarHeader>
    <RangeCalendarHeading />
    <RangeCalendarPrevButton />
    <RangeCalendarNextButton />
  </RangeCalendarHeader>
  <RangeCalendarGrid>
    <RangeCalendarGridHead>
      <RangeCalendarGridRow>
        <RangeCalendarHeadCell />
      </RangeCalendarGridRow>
    </RangeCalendarGridHead>
    <RangeCalendarGridBody>
      <RangeCalendarGridRow>
        <RangeCalendarCell>
          <RangeCalendarCellTrigger />
        </RangeCalendarCell>
      </RangeCalendarGridRow>
    </RangeCalendarGridBody>
  </RangeCalendarGrid>
</RangeCalendar>
```

## API reference

### RangeCalendar

Lets the user pick a start and end date from one or more months. Renders a `<div>`.

`data-slot="range-calendar"` · `RangeCalendar.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultPlaceholder` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The default placeholder date |
| `defaultValue` | `DateRange` |  | The default value for the calendar |
| `modelValue` | `DateRange \| null`: `null \| DateRange` |  | The controlled selected date range of the calendar. Can be bound as `v-model`. |
| `placeholder` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The placeholder date, which is used to determine what month to display when no date is selected. This updates as the user navigates the calendar and can be used to programmatically control the calendar view |
| `allowNonContiguousRanges` | `boolean` | `false` | When combined with `isDateUnavailable`, determines whether non-contiguous ranges, i.e. ranges containing unavailable dates, may be selected. |
| `pagedNavigation` | `boolean` | `false` | This property causes the previous and next buttons to navigate by the number of months displayed at once, rather than one month |
| `preventDeselect` | `boolean` | `false` | Whether or not to prevent the user from deselecting a date without selecting another date first |
| `maximumDays` | `number` |  | The maximum number of days that can be selected in a range |
| `weekStartsOn` | `WeekStartsOn`: `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` |  | The day of the week to start the calendar on |
| `weekdayFormat` | `WeekDayFormat`: `'narrow' \| 'short' \| 'long'` | `'narrow'` | The format to use for the weekday strings provided via the weekdays slot prop |
| `calendarLabel` | `string` |  | The accessible label for the calendar |
| `fixedWeeks` | `boolean` | `false` | Whether or not to always display 6 weeks in the calendar |
| `maxValue` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The maximum date that can be selected |
| `minValue` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The minimum date that can be selected |
| `locale` | `string` |  | The locale to use for formatting dates |
| `numberOfMonths` | `number` | `1` | The number of months to display at once |
| `disabled` | `boolean` | `false` | Whether or not the calendar is disabled |
| `readonly` | `boolean` | `false` | Whether or not the calendar is readonly |
| `initialFocus` | `boolean` | `false` | If true, the calendar will focus the selected day, today, or the first day of the month depending on what is visible when the calendar is mounted |
| `isDateDisabled` | `Matcher`: `(date: DateValue): boolean` |  | A function that returns whether or not a date is disabled |
| `isDateUnavailable` | `Matcher`: `(date: DateValue): boolean` |  | A function that returns whether or not a date is unavailable |
| `isDateHighlightable` | `Matcher`: `(date: DateValue): boolean` |  | A function that returns whether or not a date is hightable |
| `dir` | `Direction`: `'ltr' \| 'rtl'` |  | The reading direction of the calendar when applicable.<br><br>If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
| `nextPage` | `((placeholder: DateValue) => DateValue)`: `(placeholder: DateValue): DateValue` |  | A function that returns the next page of the calendar. It receives the current placeholder as an argument inside the component. |
| `prevPage` | `((placeholder: DateValue) => DateValue)`: `(placeholder: DateValue): DateValue` |  | A function that returns the previous page of the calendar. It receives the current placeholder as an argument inside the component. |
| `disableDaysOutsideCurrentView` | `boolean` | `false` | Whether or not to disable days outside the current view. |
| `fixedDate` | `'start' \| 'end'` |  | Which part of the range should be fixed |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `date: DateRange` | Event handler called whenever the model value changes |
| `update:placeholder` | `date: DateValue` | Event handler called whenever the placeholder value changes |
| `update:startValue` | `date: DateValue \| undefined` | Event handler called whenever the start value changes |
| `update:validModelValue` | `date: DateRange` | Event handler called whenever there is a new validModel |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-invalid` |  | Present while the value is invalid. |
| `data-readonly` |  | Present while the part is read-only. |

### RangeCalendarCell

Holds one day of the range calendar grid. Renders a `<td>`.

`data-slot="range-calendar-cell"` · `RangeCalendarCell.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `date` (required) | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The date the cell holds. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'td'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |

### RangeCalendarCellTrigger

Lets the user select a day as part of the date range. Renders a `<button>`.

`data-slot="range-calendar-trigger"` · `RangeCalendarCellTrigger.vue`

Styling: `range-calendar-cell-trigger`, which extends [`action`](https://ui.frontic.com/DESIGN.md#action), [`calendar-day`](https://ui.frontic.com/DESIGN.md#calendar-day), [`calendar-day-fill`](https://ui.frontic.com/DESIGN.md#calendar-day-fill), [`calendar-day-today`](https://ui.frontic.com/DESIGN.md#calendar-day-today) and [`calendar-day-outside`](https://ui.frontic.com/DESIGN.md#calendar-day-outside). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `day` (required) | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The date the trigger selects. |
| `month` (required) | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The month on display, so days of other months render as outside it. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'button'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-focused` |  | Present while the date has keyboard focus. |
| `data-highlighted` |  | Present while the date lies in the range being picked. |
| `data-highlighted-end` |  | Present on the last date of the range being picked. |
| `data-highlighted-start` |  | Present on the first date of the range being picked. |
| `data-outside-view` |  | Present when the date lies outside the cell's month. |
| `data-outside-visible-view` |  | Present when the date lies outside every month on display. |
| `data-selected` |  | Present while the item is selected. |
| `data-selection-end` |  | Present on the last date of the selected range. |
| `data-selection-start` |  | Present on the first date of the selected range. |
| `data-today` |  | Present on today's date. |
| `data-unavailable` |  | Present when the date cannot be picked. |
| `data-value` |  | The date the cell holds, as `YYYY-MM-DD`. |

### RangeCalendarGrid

Holds the weekday names and days of one month. Renders a `<table>`.

`data-slot="range-calendar-grid"` · `RangeCalendarGrid.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'table'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |
| `data-readonly` |  | Present while the part is read-only. |

### RangeCalendarGridBody

Holds the week rows of one month. Renders a `<tbody>`.

`data-slot="range-calendar-grid-body"` · `RangeCalendarGridBody.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'tbody'` | The element or component this component should render as. Can be overwritten by `asChild`. |

### RangeCalendarGridHead

Holds the row of weekday names above the days. Renders a `<thead>`.

`data-slot="range-calendar-grid-head"` · `RangeCalendarGridHead.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'thead'` | The element or component this component should render as. Can be overwritten by `asChild`. |

### RangeCalendarGridRow

Groups the cells of one week, or the weekday names. Renders a `<tr>`.

`data-slot="range-calendar-grid-row"` · `RangeCalendarGridRow.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'tr'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### RangeCalendarHeadCell

Labels one weekday above its column of days. Renders a `<th>`.

`data-slot="range-calendar-head-cell"` · `RangeCalendarHeadCell.vue`

Styling: `range-calendar-head-cell`, which extends [`calendar-weekday`](https://ui.frontic.com/DESIGN.md#calendar-weekday). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'th'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### RangeCalendarHeader

Groups the heading with the buttons that change the month. Renders a `<div>`.

`data-slot="range-calendar-header"` · `RangeCalendarHeader.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### RangeCalendarHeading

Shows the month and year currently displayed. Renders a `<div>`.

`data-slot="range-calendar-heading"` · `RangeCalendarHeading.vue`

Styling: `range-calendar-heading`, which extends [`calendar-month`](https://ui.frontic.com/DESIGN.md#calendar-month). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `default` | `{ headingValue: string; }` | Replaces the heading text; gets the month and year as `headingValue`. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |

### RangeCalendarNextButton

Moves the range calendar to the next month. Renders a `<button>`.

`data-slot="range-calendar-next-button"` · `RangeCalendarNextButton.vue`

Styling: `range-calendar-nav`, which extends [`action`](https://ui.frontic.com/DESIGN.md#action) and [`calendar-nav-button`](https://ui.frontic.com/DESIGN.md#calendar-nav-button). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `nextPage` | `((placeholder: DateValue) => DateValue)`: `(placeholder: DateValue): DateValue` |  | The function to be used for the next page. Overwrites the `nextPage` function set on the `RangeCalendarRoot`. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'button'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |

### RangeCalendarPrevButton

Moves the range calendar to the previous month. Renders a `<button>`.

`data-slot="range-calendar-prev-button"` · `RangeCalendarPrevButton.vue`

Styling: `range-calendar-nav`, which extends [`action`](https://ui.frontic.com/DESIGN.md#action) and [`calendar-nav-button`](https://ui.frontic.com/DESIGN.md#calendar-nav-button). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `prevPage` | `((placeholder: DateValue) => DateValue)`: `(placeholder: DateValue): DateValue` |  | The function to be used for the prev page. Overwrites the `prevPage` function set on the `RangeCalendarRoot`. |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'button'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-disabled` |  | Present while the part is disabled. |

## Styling

| Part | Recipe |
| --- | --- |
| `grid-row` | [`range-calendar-grid-row`](https://ui.frontic.com/DESIGN.md#range-calendar-grid-row) |
| `default` | [`range-calendar`](https://ui.frontic.com/DESIGN.md#range-calendar) |
| `header` | [`range-calendar-header`](https://ui.frontic.com/DESIGN.md#range-calendar-header) |
| `cell` | [`range-calendar-cell`](https://ui.frontic.com/DESIGN.md#range-calendar-cell) |
| `cell-trigger` | [`range-calendar-cell-trigger`](https://ui.frontic.com/DESIGN.md#range-calendar-cell-trigger) |
| `nav` | [`range-calendar-nav`](https://ui.frontic.com/DESIGN.md#range-calendar-nav) |
| `head-cell` | [`range-calendar-head-cell`](https://ui.frontic.com/DESIGN.md#range-calendar-head-cell) |
| `heading` | [`range-calendar-heading`](https://ui.frontic.com/DESIGN.md#range-calendar-heading) |
| `focus-ring` | [`focus-ring`](https://ui.frontic.com/DESIGN.md#focus-ring) |
| `invalid` | [`invalid`](https://ui.frontic.com/DESIGN.md#invalid) |
| `disabled` | [`disabled`](https://ui.frontic.com/DESIGN.md#disabled) |

States with a look of their own: hover, focus, disabled and invalid.

## Source

`components/ui/range-calendar/RangeCalendar.vue`

```vue
<script lang="ts">
/** Lets the user pick a start and end date from one or more months. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarRootEmits, RangeCalendarRootProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarRoot, useForwardPropsEmits } from 'reka-ui'
import {
  RangeCalendarCell,
  RangeCalendarCellTrigger,
  RangeCalendarGrid,
  RangeCalendarGridBody,
  RangeCalendarGridHead,
  RangeCalendarGridRow,
  RangeCalendarHeadCell,
  RangeCalendarHeader,
  RangeCalendarHeading,
  RangeCalendarNextButton,
  RangeCalendarPrevButton,
} from '.'

const props = defineProps<RangeCalendarRootProps & { class?: HTMLAttributes['class'] }>()

const emits = defineEmits<RangeCalendarRootEmits>()

const delegatedProps = reactiveOmit(props, 'class')

const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
  <RangeCalendarRoot v-slot="{ grid, weekDays }" data-slot="range-calendar" :class="['fui-range-calendar', props.class]" v-bind="forwarded">
    <RangeCalendarHeader>
      <RangeCalendarHeading />

      <div class="flex items-center gap-1">
        <RangeCalendarPrevButton />
        <RangeCalendarNextButton />
      </div>
    </RangeCalendarHeader>

    <div class="mt-4 flex flex-wrap justify-center gap-4">
      <RangeCalendarGrid v-for="month in grid" :key="month.value.toString()">
        <RangeCalendarGridHead>
          <RangeCalendarGridRow>
            <RangeCalendarHeadCell v-for="day in weekDays" :key="day">
              {{ day }}
            </RangeCalendarHeadCell>
          </RangeCalendarGridRow>
        </RangeCalendarGridHead>
        <RangeCalendarGridBody>
          <RangeCalendarGridRow v-for="(weekDates, index) in month.rows" :key="`weekDate-${index}`" class="mt-2 w-full">
            <RangeCalendarCell v-for="weekDate in weekDates" :key="weekDate.toString()" :date="weekDate">
              <RangeCalendarCellTrigger :day="weekDate" :month="month.value" />
            </RangeCalendarCell>
          </RangeCalendarGridRow>
        </RangeCalendarGridBody>
      </RangeCalendarGrid>
    </div>
  </RangeCalendarRoot>
</template>
```

`components/ui/range-calendar/RangeCalendarCell.vue`

```vue
<script lang="ts">
/** Holds one day of the range calendar grid. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarCellProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { cva } from 'class-variance-authority'
import { injectRangeCalendarRootContext, RangeCalendarCell, useForwardProps } from 'reka-ui'
import { computed } from 'vue'

const props = defineProps<RangeCalendarCellProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
const cell = cva('fui-range-calendar-cell relative focus-within:relative focus-within:z-20', {
  variants: {
    edge: {
      none: 'fui-range-calendar-cell-none',
      start: 'fui-range-calendar-cell-start',
      end: 'fui-range-calendar-cell-end',
      both: 'fui-range-calendar-cell-both',
    },
  },
  defaultVariants: { edge: 'none' },
})

const root = injectRangeCalendarRootContext()
const edge = computed(() => {
  const start = root.isSelectionStart(props.date)
  const end = root.isSelectionEnd(props.date)
  return start && end ? 'both' : start ? 'start' : end ? 'end' : 'none'
})
</script>

<template>
  <RangeCalendarCell data-slot="range-calendar-cell" :class="[cell({ edge }), props.class]" v-bind="forwardedProps">
    <slot />
  </RangeCalendarCell>
</template>
```

`components/ui/range-calendar/RangeCalendarCellTrigger.vue`

```vue
<script lang="ts">
/** Lets the user select a day as part of the date range. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarCellTriggerProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { cva } from 'class-variance-authority'
import { getLocalTimeZone, isSameMonth, isToday } from '@internationalized/date'
import { injectRangeCalendarRootContext, RangeCalendarCellTrigger, useForwardProps } from 'reka-ui'
import { computed } from 'vue'

const props = withDefaults(defineProps<RangeCalendarCellTriggerProps & { class?: HTMLAttributes['class'] }>(), {
  as: 'button',
})

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
const cellTrigger = cva(
  'fui-range-calendar-cell-trigger group/button whitespace-nowrap bg-clip-padding outline-none transition-all select-none focus-visible:fui-focus-ring aria-invalid:fui-invalid disabled:pointer-events-none aria-disabled:fui-disabled data-unavailable:line-through **:data-icon:pointer-events-none **:data-icon:shrink-0',
  {
    variants: {
      day: {
        current: 'fui-range-calendar-cell-trigger-current',
        today: 'fui-range-calendar-cell-trigger-today',
        outside: 'fui-range-calendar-cell-trigger-outside',
      },
      position: {
        middle: 'fui-range-calendar-cell-trigger-middle',
        edge: 'fui-range-calendar-cell-trigger-edge',
      },
    },
    defaultVariants: { day: 'current', position: 'middle' },
  },
)

const day = computed(() => (isToday(props.day, getLocalTimeZone()) ? 'today' : isSameMonth(props.day, props.month) ? 'current' : 'outside'))
const root = injectRangeCalendarRootContext()
const position = computed(() => (root.isSelectionStart(props.day) || root.isSelectionEnd(props.day) ? 'edge' : 'middle'))
</script>

<template>
  <RangeCalendarCellTrigger data-slot="range-calendar-trigger" :class="[cellTrigger({ day, position }), props.class]" v-bind="forwardedProps">
    <slot />
  </RangeCalendarCellTrigger>
</template>
```

`components/ui/range-calendar/RangeCalendarGrid.vue`

```vue
<script lang="ts">
/** Holds the weekday names and days of one month. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarGridProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarGrid, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarGridProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarGrid data-slot="range-calendar-grid" :class="['border-collapse space-x-1', props.class]" v-bind="forwardedProps">
    <slot />
  </RangeCalendarGrid>
</template>
```

`components/ui/range-calendar/RangeCalendarGridBody.vue`

```vue
<script lang="ts">
/** Holds the week rows of one month. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarGridBodyProps } from 'reka-ui'
import { RangeCalendarGridBody, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarGridBodyProps>()
const forwarded = useForwardProps(props)
</script>

<template>
  <RangeCalendarGridBody data-slot="range-calendar-grid-body" v-bind="forwarded">
    <slot />
  </RangeCalendarGridBody>
</template>
```

`components/ui/range-calendar/RangeCalendarGridHead.vue`

```vue
<script lang="ts">
/** Holds the row of weekday names above the days. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarGridHeadProps } from 'reka-ui'
import { RangeCalendarGridHead, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarGridHeadProps>()
const forwarded = useForwardProps(props)
</script>

<template>
  <RangeCalendarGridHead data-slot="range-calendar-grid-head" v-bind="forwarded">
    <slot />
  </RangeCalendarGridHead>
</template>
```

`components/ui/range-calendar/RangeCalendarGridRow.vue`

```vue
<script lang="ts">
/** Groups the cells of one week, or the weekday names. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarGridRowProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarGridRow, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarGridRowProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarGridRow data-slot="range-calendar-grid-row" :class="['fui-range-calendar-grid-row', props.class]" v-bind="forwardedProps">
    <slot />
  </RangeCalendarGridRow>
</template>
```

`components/ui/range-calendar/RangeCalendarHeadCell.vue`

```vue
<script lang="ts">
/** Labels one weekday above its column of days. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarHeadCellProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarHeadCell, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarHeadCellProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarHeadCell data-slot="range-calendar-head-cell" :class="['fui-range-calendar-head-cell', props.class]" v-bind="forwardedProps">
    <slot />
  </RangeCalendarHeadCell>
</template>
```

`components/ui/range-calendar/RangeCalendarHeader.vue`

```vue
<script lang="ts">
/** Groups the heading with the buttons that change the month. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarHeaderProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarHeader, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarHeaderProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarHeader data-slot="range-calendar-header" :class="['fui-range-calendar-header relative', props.class]" v-bind="forwardedProps">
    <slot />
  </RangeCalendarHeader>
</template>
```

`components/ui/range-calendar/RangeCalendarHeading.vue`

```vue
<script lang="ts">
/** Shows the month and year currently displayed. */
export default {}
</script>

<script lang="ts" setup>
import type { RangeCalendarHeadingProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarHeading, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarHeadingProps & { class?: HTMLAttributes['class'] }>()

defineSlots<{
  /** Replaces the heading text; gets the month and year as `headingValue`. */
  default: (props: { headingValue: string }) => any
}>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarHeading v-slot="{ headingValue }" data-slot="range-calendar-heading" :class="['fui-range-calendar-heading', props.class]" v-bind="forwardedProps">
    <slot :heading-value>
      {{ headingValue }}
    </slot>
  </RangeCalendarHeading>
</template>
```

`components/ui/range-calendar/RangeCalendarNextButton.vue`

```vue
<script lang="ts">
/** Moves the range calendar to the next month. */
export default {}
</script>

<script lang="ts" setup>
import { ChevronRightIcon } from '@lucide/vue'

import type { RangeCalendarNextProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarNext, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarNextProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarNext
    data-slot="range-calendar-next-button"
    :class="[
      'fui-range-calendar-nav group/button absolute inset-e-1 whitespace-nowrap bg-clip-padding outline-none transition-all select-none focus-visible:fui-focus-ring aria-invalid:fui-invalid disabled:pointer-events-none disabled:fui-disabled **:data-icon:pointer-events-none **:data-icon:shrink-0',
      props.class,
    ]"
    v-bind="forwardedProps"
  >
    <slot>
      <ChevronRightIcon class="rtl:-scale-x-100" data-icon />
    </slot>
  </RangeCalendarNext>
</template>
```

`components/ui/range-calendar/RangeCalendarPrevButton.vue`

```vue
<script lang="ts">
/** Moves the range calendar to the previous month. */
export default {}
</script>

<script lang="ts" setup>
import { ChevronLeftIcon } from '@lucide/vue'

import type { RangeCalendarPrevProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { reactiveOmit } from '@vueuse/core'
import { RangeCalendarPrev, useForwardProps } from 'reka-ui'

const props = defineProps<RangeCalendarPrevProps & { class?: HTMLAttributes['class'] }>()

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <RangeCalendarPrev
    data-slot="range-calendar-prev-button"
    :class="[
      'fui-range-calendar-nav group/button absolute inset-s-1 whitespace-nowrap bg-clip-padding outline-none transition-all select-none focus-visible:fui-focus-ring aria-invalid:fui-invalid disabled:pointer-events-none disabled:fui-disabled **:data-icon:pointer-events-none **:data-icon:shrink-0',
      props.class,
    ]"
    v-bind="forwardedProps"
  >
    <slot>
      <ChevronLeftIcon class="rtl:-scale-x-100" data-icon />
    </slot>
  </RangeCalendarPrev>
</template>
```
