---
name: calendar
title: Calendar
url: https://ui.frontic.com/components/ui/calendar.md
tier: ui
install: npx @frontic/ui add calendar
files:
  - components/ui/calendar/Calendar.vue
  - components/ui/calendar/CalendarCell.vue
  - components/ui/calendar/CalendarCellTrigger.vue
  - components/ui/calendar/CalendarGrid.vue
  - components/ui/calendar/CalendarGridBody.vue
  - components/ui/calendar/CalendarGridHead.vue
  - components/ui/calendar/CalendarGridRow.vue
  - components/ui/calendar/CalendarHeadCell.vue
  - components/ui/calendar/CalendarHeader.vue
  - components/ui/calendar/CalendarHeading.vue
  - components/ui/calendar/CalendarNextButton.vue
  - components/ui/calendar/CalendarPrevButton.vue
  - components/ui/calendar/index.ts
registryDependencies:
  - native-select
dependencies:
  - "@internationalized/date"
  - "@lucide/vue"
  - "@vueuse/core"
  - class-variance-authority
  - reka-ui
---

# Calendar

Lets the shopper pick a date from a month grid, within a minimum and maximum, starting the week where the locale it is given does, one or several months side by side.

## Install

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

Built from: [Native Select](https://ui.frontic.com/components/ui/native-select.md)

Used by: [Date Picker](https://ui.frontic.com/components/ui/date-picker.md), [Delivery Slot](https://ui.frontic.com/components/sections/delivery-slot.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 { DateValue } from 'reka-ui'
import { shallowRef } from 'vue'
import { Calendar } from '@/components/ui/calendar'

// A few days from today, stepping back instead near the month's end so the calendar opens on today's month.
const near = (days: number) => {
  const now = today(getLocalTimeZone())
  const ahead = now.add({ days })
  return ahead.month === now.month ? ahead : now.subtract({ days })
}
const delivery = shallowRef<DateValue>(near(3))
const pickup = shallowRef<DateValue>(near(5))
</script>

<template>
  <div class="flex flex-wrap items-start gap-8">
    <div class="border border-border p-4">
      <Calendar v-model="delivery" />
    </div>
    <div class="border border-border p-4">
      <Calendar v-model="pickup" layout="month-and-year" />
    </div>
  </div>
</template>
```

## Anatomy

```vue
<Calendar>
  <CalendarHeader>
    <CalendarHeading />
    <CalendarPrevButton />
    <CalendarNextButton />
  </CalendarHeader>
  <CalendarGrid>
    <CalendarGridHead>
      <CalendarGridRow>
        <CalendarHeadCell />
      </CalendarGridRow>
    </CalendarGridHead>
    <CalendarGridBody>
      <CalendarGridRow>
        <CalendarCell>
          <CalendarCellTrigger />
        </CalendarCell>
      </CalendarGridRow>
    </CalendarGridBody>
  </CalendarGrid>
</Calendar>
```

## API reference

### Calendar

Lets the user pick a date from a monthly calendar. Renders a `<div>`.

`data-slot="calendar"` · `Calendar.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultValue` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The default value for the calendar |
| `defaultPlaceholder` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The default placeholder date |
| `placeholder` | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The placeholder date, which is used to determine what month to display when no date is 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 |
| `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 the calendar is disabled |
| `readonly` | `boolean` | `false` | Whether 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 |
| `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. |
| `modelValue` | `DateValue \| DateValue[] \| null`: `null \| CalendarDate \| CalendarDateTime \| ZonedDateTime \| DateValue[]` |  | The controlled selected date value of the calendar. Can be bound as `v-model`. |
| `multiple` | `boolean` | `false` | Whether multiple dates can be selected |
| `disableDaysOutsideCurrentView` | `boolean` | `false` | Whether or not to disable days outside the current view. |
| `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. |
| `layout` | `LayoutTypes`: `'month-and-year' \| 'month-only' \| 'year-only'` |  | Replaces the heading with month and/or year dropdowns for jumping across dates; the plain month heading when left out. |
| `yearRange` | `DateValue[]` |  | The years the year dropdown offers; when left out, from `minValue` (or 100 years back) to `maxValue` (or 10 years ahead). |

| Event | Payload | Description |
| --- | --- | --- |
| `update:modelValue` | `date: DateValue \| undefined` | Event handler called whenever the model value changes |
| `update:placeholder` | `date: DateValue` | Event handler called whenever the placeholder value changes |

| Slot | Slot props | Description |
| --- | --- | --- |
| `calendar-heading` | `{ date: DateValue; month: ReuseTemplateComponent<{ date: DateValue; }, Record<"default", undefined>>; year: ReuseTemplateComponent<{ date: DateValue; }, Record<"default", undefined>>; }` | Replaces the month heading; gets the displayed month's date and the month and year dropdowns to place. |
| `calendar-prev-icon` |  | Replaces the icon on the previous-month button. |
| `calendar-next-icon` |  | Replaces the icon on the next-month button. |

| 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. |

### CalendarCell

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

`data-slot="calendar-cell"` · `CalendarCell.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `date` (required) | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The date value for the cell |
| `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. |

### CalendarCellTrigger

Lets the user select a single day of the calendar. Renders a `<button>`.

`data-slot="calendar-cell-trigger"` · `CalendarCellTrigger.vue`

Styling: `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-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 value provided to the cell trigger |
| `month` (required) | `DateValue`: `CalendarDate \| CalendarDateTime \| ZonedDateTime` |  | The month in which the cell is rendered |
| `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-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-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`. |

### CalendarGrid

Lays out the days of one month in a grid. Renders a `<table>`.

`data-slot="calendar-grid"` · `CalendarGrid.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. |

### CalendarGridBody

Holds the weeks of the calendar grid. Renders a `<tbody>`.

`data-slot="calendar-grid-body"` · `CalendarGridBody.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`. |

### CalendarGridHead

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

`data-slot="calendar-grid-head"` · `CalendarGridHead.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`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### CalendarGridRow

Groups the days of one week in the calendar grid. Renders a `<tr>`.

`data-slot="calendar-grid-row"` · `CalendarGridRow.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. |

### CalendarHeadCell

Labels one column of the calendar grid with a weekday name. Renders a `<th>`.

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

Styling: `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. |

### CalendarHeader

Groups the heading and month navigation above the calendar grid. Renders a `<div>`.

`data-slot="calendar-header"` · `CalendarHeader.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. |

### CalendarHeading

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

`data-slot="calendar-heading"` · `CalendarHeading.vue`

Styling: `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 formatted month and year. |

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

### CalendarNextButton

Lets the user move the calendar to the next month. Renders a `<button>`.

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

Styling: `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 `CalendarRoot`. |
| `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. |

### CalendarPrevButton

Lets the user move the calendar to the previous month. Renders a `<button>`.

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

Styling: `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 `CalendarRoot`. |
| `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. |

## Exports

```ts
import { type LayoutTypes } from '@/components/ui/calendar'
```

- `LayoutTypes`: `'month-and-year' \| 'month-only' \| 'year-only' \| undefined`

## Styling

| Part | Recipe |
| --- | --- |
| `grid-row` | [`calendar-grid-row`](https://ui.frontic.com/DESIGN.md#calendar-grid-row) |
| `default` | [`calendar`](https://ui.frontic.com/DESIGN.md#calendar) |
| `header` | [`calendar-header`](https://ui.frontic.com/DESIGN.md#calendar-header) |
| `cell` | [`calendar-cell`](https://ui.frontic.com/DESIGN.md#calendar-cell) |
| `cell-trigger` | [`calendar-cell-trigger`](https://ui.frontic.com/DESIGN.md#calendar-cell-trigger) |
| `head-cell` | [`calendar-head-cell`](https://ui.frontic.com/DESIGN.md#calendar-head-cell) |
| `heading` | [`calendar-heading`](https://ui.frontic.com/DESIGN.md#calendar-heading) |
| `nav` | [`calendar-nav`](https://ui.frontic.com/DESIGN.md#calendar-nav) |
| `select-label` | [`calendar-select-label`](https://ui.frontic.com/DESIGN.md#calendar-select-label) |
| `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/calendar/Calendar.vue`

```vue
<script lang="ts">
/** Lets the user pick a date from a monthly calendar. */
export default {}
</script>

<script lang="ts" setup>
import type { CalendarRootEmits, CalendarRootProps, DateValue } from 'reka-ui'
import type { HTMLAttributes, Ref } from 'vue'
import type { LayoutTypes } from '.'
import { getLocalTimeZone, today } from '@internationalized/date'
import type { ReuseTemplateComponent } from '@vueuse/core'
import { createReusableTemplate, reactiveOmit, useVModel } from '@vueuse/core'
import { CalendarRoot, useDateFormatter, useForwardPropsEmits, useLocale } from 'reka-ui'
import { createYear, createYearRange, toDate } from 'reka-ui/date'
import { computed, toRaw, toRef, watch } from 'vue'
import { NativeSelect, NativeSelectOption } from '@library/components/ui/native-select'
import {
  CalendarCell,
  CalendarCellTrigger,
  CalendarGrid,
  CalendarGridBody,
  CalendarGridHead,
  CalendarGridRow,
  CalendarHeadCell,
  CalendarHeader,
  CalendarHeading,
  CalendarNextButton,
  CalendarPrevButton,
} from '.'

const props = withDefaults(
  defineProps<
    CalendarRootProps & {
      class?: HTMLAttributes['class']
      /** Replaces the heading with month and/or year dropdowns for jumping across dates; the plain month heading when left out. */
      layout?: LayoutTypes
      /** The years the year dropdown offers; when left out, from `minValue` (or 100 years back) to `maxValue` (or 10 years ahead). */
      yearRange?: DateValue[]
    }
  >(),
  {
    modelValue: undefined,
    layout: undefined,
  },
)
const emits = defineEmits<CalendarRootEmits>()

defineSlots<{
  /** Replaces the month heading; gets the displayed month's date and the month and year dropdowns to place. */
  'calendar-heading'?: (props: {
    date: DateValue
    month: ReuseTemplateComponent<{ date: DateValue }, Record<'default', undefined>>
    year: ReuseTemplateComponent<{ date: DateValue }, Record<'default', undefined>>
  }) => unknown
  /** Replaces the icon on the previous-month button. */
  'calendar-prev-icon'?: () => unknown
  /** Replaces the icon on the next-month button. */
  'calendar-next-icon'?: () => unknown
}>()

const delegatedProps = reactiveOmit(props, 'class', 'layout', 'placeholder')

const placeholder = useVModel(props, 'placeholder', emits, {
  passive: true,
  defaultValue: props.defaultPlaceholder ?? today(getLocalTimeZone()),
}) as Ref<DateValue>

const locale = useLocale(toRef(() => props.locale))
const formatter = useDateFormatter(locale.value)
watch(locale, (value) => formatter.setLocale(value))

const yearRange = computed(() => {
  return (
    props.yearRange ??
    createYearRange({
      start: props?.minValue ?? (toRaw(props.placeholder) ?? props.defaultPlaceholder ?? today(getLocalTimeZone())).cycle('year', -100),

      end: props?.maxValue ?? (toRaw(props.placeholder) ?? props.defaultPlaceholder ?? today(getLocalTimeZone())).cycle('year', 10),
    })
  )
})

const [DefineMonthTemplate, ReuseMonthTemplate] = createReusableTemplate<{ date: DateValue }>()
const [DefineYearTemplate, ReuseYearTemplate] = createReusableTemplate<{ date: DateValue }>()

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

<template>
  <DefineMonthTemplate v-slot="{ date }">
    <div class="[&_[data-slot=native-select-icon]]:inset-e-1">
      <div class="relative">
        <div class="fui-calendar-select-label pointer-events-none absolute inset-0 flex h-full items-center">
          {{ formatter.custom(toDate(date), { month: 'short' }) }}
        </div>
        <NativeSelect
          size="sm"
          class="relative text-transparent"
          @change="
            (e: Event) => {
              placeholder = placeholder.set({
                month: Number((e?.target as any)?.value),
              })
            }
          "
        >
          <NativeSelectOption v-for="month in createYear({ dateObj: date })" :key="month.toString()" :value="month.month" :selected="date.month === month.month">
            {{ formatter.custom(toDate(month), { month: 'short' }) }}
          </NativeSelectOption>
        </NativeSelect>
      </div>
    </div>
  </DefineMonthTemplate>

  <DefineYearTemplate v-slot="{ date }">
    <div class="[&_[data-slot=native-select-icon]]:inset-e-1">
      <div class="relative">
        <div class="fui-calendar-select-label pointer-events-none absolute inset-0 flex h-full items-center">
          {{ formatter.custom(toDate(date), { year: 'numeric' }) }}
        </div>
        <NativeSelect
          size="sm"
          class="relative text-transparent"
          @change="
            (e: Event) => {
              placeholder = placeholder.set({
                year: Number((e?.target as any)?.value),
              })
            }
          "
        >
          <NativeSelectOption v-for="year in yearRange" :key="year.toString()" :value="year.year" :selected="date.year === year.year">
            {{ formatter.custom(toDate(year), { year: 'numeric' }) }}
          </NativeSelectOption>
        </NativeSelect>
      </div>
    </div>
  </DefineYearTemplate>

  <CalendarRoot v-slot="{ grid, weekDays, date }" v-bind="forwarded" v-model:placeholder="placeholder" data-slot="calendar" :class="['fui-calendar group/calendar', props.class]">
    <CalendarHeader>
      <slot name="calendar-heading" :date="date" :month="ReuseMonthTemplate" :year="ReuseYearTemplate">
        <template v-if="layout === 'month-and-year'">
          <div class="flex items-center justify-center gap-1">
            <ReuseMonthTemplate :date="date" />
            <ReuseYearTemplate :date="date" />
          </div>
        </template>
        <template v-else-if="layout === 'month-only'">
          <div class="flex items-center justify-center gap-1">
            <ReuseMonthTemplate :date="date" />
            {{ formatter.custom(toDate(date), { year: 'numeric' }) }}
          </div>
        </template>
        <template v-else-if="layout === 'year-only'">
          <div class="flex items-center justify-center gap-1">
            {{ formatter.custom(toDate(date), { month: 'short' }) }}
            <ReuseYearTemplate :date="date" />
          </div>
        </template>
        <template v-else>
          <CalendarHeading />
        </template>
      </slot>

      <nav class="pointer-events-none flex items-center gap-1 absolute top-0 inset-x-0 justify-between">
        <CalendarPrevButton>
          <slot name="calendar-prev-icon" />
        </CalendarPrevButton>
        <CalendarNextButton>
          <slot name="calendar-next-icon" />
        </CalendarNextButton>
      </nav>
    </CalendarHeader>

    <div class="mt-4 flex flex-wrap justify-center gap-4">
      <CalendarGrid v-for="month in grid" :key="month.value.toString()">
        <CalendarGridHead>
          <CalendarGridRow>
            <CalendarHeadCell v-for="day in weekDays" :key="day">
              {{ day }}
            </CalendarHeadCell>
          </CalendarGridRow>
        </CalendarGridHead>
        <CalendarGridBody>
          <CalendarGridRow v-for="(weekDates, index) in month.rows" :key="`weekDate-${index}`" class="mt-2 w-full">
            <CalendarCell v-for="weekDate in weekDates" :key="weekDate.toString()" :date="weekDate">
              <CalendarCellTrigger :day="weekDate" :month="month.value" />
            </CalendarCell>
          </CalendarGridRow>
        </CalendarGridBody>
      </CalendarGrid>
    </div>
  </CalendarRoot>
</template>
```

`components/ui/calendar/CalendarCell.vue`

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

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <CalendarCell data-slot="calendar-cell" :class="['fui-calendar-cell relative focus-within:relative focus-within:z-20', props.class]" v-bind="forwardedProps">
    <slot />
  </CalendarCell>
</template>
```

`components/ui/calendar/CalendarCellTrigger.vue`

```vue
<script lang="ts">
/** Lets the user select a single day of the calendar. */
export default {}
</script>

<script lang="ts" setup>
import type { CalendarCellTriggerProps } 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 { CalendarCellTrigger, useForwardProps } from 'reka-ui'
import { computed } from 'vue'

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
const cellTrigger = cva(
  'fui-calendar-cell-trigger group/button cursor-default 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-calendar-cell-trigger-current',
        today: 'fui-calendar-cell-trigger-today',
        outside: 'fui-calendar-cell-trigger-outside',
      },
    },
    defaultVariants: { day: 'current' },
  },
)

const day = computed(() => (isToday(props.day, getLocalTimeZone()) ? 'today' : isSameMonth(props.day, props.month) ? 'current' : 'outside'))
</script>

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

`components/ui/calendar/CalendarGrid.vue`

```vue
<script lang="ts">
/** Lays out the days of one month in a grid. */
export default {}
</script>

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

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

`components/ui/calendar/CalendarGridBody.vue`

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

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

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

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

`components/ui/calendar/CalendarGridHead.vue`

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

<script lang="ts" setup>
import type { CalendarGridHeadProps } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { CalendarGridHead, useForwardProps } from 'reka-ui'

const props = defineProps<CalendarGridHeadProps & { class?: HTMLAttributes['class'] }>()
const forwarded = useForwardProps(props)
</script>

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

`components/ui/calendar/CalendarGridRow.vue`

```vue
<script lang="ts">
/** Groups the days of one week in the calendar grid. */
export default {}
</script>

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

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

`components/ui/calendar/CalendarHeadCell.vue`

```vue
<script lang="ts">
/** Labels one column of the calendar grid with a weekday name. */
export default {}
</script>

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

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

`components/ui/calendar/CalendarHeader.vue`

```vue
<script lang="ts">
/** Groups the heading and month navigation above the calendar grid. */
export default {}
</script>

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

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

`components/ui/calendar/CalendarHeading.vue`

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

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

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

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

`components/ui/calendar/CalendarNextButton.vue`

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

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

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <CalendarNext
    data-slot="calendar-next-button"
    :class="[
      'fui-calendar-nav group/button whitespace-nowrap bg-clip-padding outline-none transition-all select-none pointer-events-auto 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>
  </CalendarNext>
</template>
```

`components/ui/calendar/CalendarPrevButton.vue`

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

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

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

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

const delegatedProps = reactiveOmit(props, 'class')

const forwardedProps = useForwardProps(delegatedProps)
</script>

<template>
  <CalendarPrev
    data-slot="calendar-prev-button"
    :class="[
      'fui-calendar-nav group/button whitespace-nowrap bg-clip-padding outline-none transition-all select-none pointer-events-auto 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>
  </CalendarPrev>
</template>
```

`components/ui/calendar/index.ts`

```ts
export { default as Calendar } from './Calendar.vue'
export { default as CalendarCell } from './CalendarCell.vue'
export { default as CalendarCellTrigger } from './CalendarCellTrigger.vue'
export { default as CalendarGrid } from './CalendarGrid.vue'
export { default as CalendarGridBody } from './CalendarGridBody.vue'
export { default as CalendarGridHead } from './CalendarGridHead.vue'
export { default as CalendarGridRow } from './CalendarGridRow.vue'
export { default as CalendarHeadCell } from './CalendarHeadCell.vue'
export { default as CalendarHeader } from './CalendarHeader.vue'
export { default as CalendarHeading } from './CalendarHeading.vue'
export { default as CalendarNextButton } from './CalendarNextButton.vue'
export { default as CalendarPrevButton } from './CalendarPrevButton.vue'

export type LayoutTypes = 'month-and-year' | 'month-only' | 'year-only' | undefined
```
