Components

Calendar

Locale-aware date grid with keyboard navigation, constraints, and single, multiple, or range selection.

Calendar renders a date grid with navigation, selection, locale formatting, constraints, and roving focus. It uses @internationalized/date values and avoids JavaScript Date parsing and local-midnight conversions.

Use Calendar for date selection from a visible month. Slots expose labels, dates, state, and actions from one component.

Date values come from @internationalized/date. Install it beside Akaza UI when application code creates or transforms dates: pnpm add akaza-ui @internationalized/date.

Anatomy

  • #previous: Previous-page control content with disabled state.
  • #heading: Visible month or month-range heading with generated month data.
  • #next: Next-page control content with disabled state.
  • #weekday: Weekday heading content with date and formatted label.
  • #day: Day button content with date, selection, range, today, outside, disabled, unavailable, and focus state.
  • #footer: Optional content after all grids with current value and placeholder.

Calendar generates the root, navigation buttons, live heading, one or more ARIA grids, rows, cells, and day buttons. Style every generated part through ui.

Usage

August 2026
SMTWTFS

Selected: 2026-08-12

Examples

Date constraints

Use minValue and maxValue for hard boundaries. isDateDisabled applies application rules while isDateUnavailable marks dates that exist but cannot be selected, such as fully booked days.

<Calendar
  v-model="date"
  :min-value="new CalendarDate(2026, 8, 5)"
  :max-value="new CalendarDate(2026, 8, 24)"
  :is-date-unavailable="(date) => closedDates.has(date.toString())"
/>

Disabled and unavailable days remain keyboard discoverable and expose aria-disabled; activation does not change the model.

Popover calendar

Compose Calendar with Popover when date selection starts from a separate button. Popover owns positioning and dismissal; Calendar keeps date focus, navigation, and selection behavior.

<script setup lang="ts">
import type { CalendarDateValue } from "akaza-ui";
import { Calendar, Popover } from "akaza-ui";
import { ref, shallowRef } from "vue";

const date = shallowRef<CalendarDateValue>();
const open = ref(false);
</script>

<template>
<Popover v-model="open" teleport="body">
  <template #trigger="{ toggle, triggerProps }">
    <button v-bind="triggerProps" @click="toggle">
      {{ date?.toString() ?? "Choose date" }}
    </button>
  </template>

  <template #content>
    <Calendar
      v-model="date"
      prevent-deselect
      @value-change="open = false"
    />
  </template>
</Popover>
</template>

Use Date Field's showCalendar prop instead when users also need segmented keyboard entry and native form integration.

Multiple dates

Set selectionMode="multiple" to bind an array. Selecting an existing date removes it unless preventDeselect is enabled. The older multiple boolean remains as a compatibility alias.

<Calendar v-model="dates" selection-mode="multiple" />

Date range

Use selectionMode="range" with a { start, end } model. First activation sets the anchor; pointer hover or keyboard focus previews the pending range; second activation commits normalized start and end values.

<script setup lang="ts">
import type { CalendarDateRange } from "akaza-ui";
import { shallowRef } from "vue";

const range = shallowRef<CalendarDateRange>({});
</script>

<template>
  <Calendar
    v-model="range"
    selection-mode="range"
    :maximum-days="14"
  />
</template>

Ranges containing disabled or unavailable dates are rejected by default. Set allowNonContiguousRanges when unavailable dates may remain inside a range; range endpoints must still be selectable.

Multiple months

numberOfMonths renders adjacent grids. Add pagedNavigation when previous and next should move by the full visible month count.

<Calendar
  v-model="date"
  :number-of-months="2"
  paged-navigation
  fixed-weeks
/>

Locale and week start

Locale controls month, weekday, and accessible date labels. Override the locale-derived first day when product requirements demand it.

<Calendar
  locale="fr-FR"
  week-starts-on="mon"
  weekday-format="short"
/>

Outside days

Outside days are rendered to keep complete weeks. Set disableDaysOutsideCurrentView to make them non-selectable while preserving keyboard context.

<Calendar disable-days-outside-current-view />

Custom day content

The day slot changes button content without replacing its keyboard and ARIA behavior.

<Calendar v-model="date">
  <template #day="{ label, today, unavailable }">
    <span :class="{ 'font-bold': today, 'line-through': unavailable }">
      {{ label }}
    </span>
  </template>
</Calendar>

Cancel changes

Both selection and visible-month changes emit cancelable details before updating their models.

<Calendar
  v-model="date"
  @value-change="(_value, details) => {
    if (hasUnsavedChanges) details.cancel()
  }"
/>

Controlled pages and initial focus

The initial focus target follows the displayed placeholder instead of assuming today's month is visible. Controlled paging keeps one eligible day as the grid tab stop without moving DOM focus when props change. Pages with no enabled days have no day tab stop, so keep the navigation controls accessible.

Date formatters are reused per component and follow the value's calendar as well as the requested locale.

API Reference

Models

ModelTypeDefaultDescription
v-modelCalendarDateValue | CalendarDateValue[] | CalendarDateRange | undefinedundefinedSelected single date, independent dates, or { start, end } range according to selectionMode.
v-model:placeholderCalendarDateValueselected date or todayMonth used as the start of the visible view.

Props

PropTypeDefaultDescription
localestring"en-US"Locale used for labels and week calculations.
dir"ltr" | "rtl""ltr"Reading direction and horizontal arrow behavior.
calendarLabelstring"Calendar"Accessible root label prefix.
weekStartsOn"sun" | "mon" | ...locale defaultExplicit first day of week.
weekdayFormat"narrow" | "short" | "long""narrow"Visible weekday label format.
fixedWeeksbooleanfalseAlways renders six weeks per month.
numberOfMonthsnumber1Adjacent month grids to render.
pagedNavigationbooleanfalseNavigates by numberOfMonths instead of one month.
preventDeselectbooleanfalsePrevents removing an already selected date.
selectionMode"single" | "multiple" | "range""single"Selects model shape and interaction behavior.
multiplebooleanfalseDeprecated compatibility alias for selectionMode="multiple".
allowNonContiguousRangesbooleanfalseAllows unavailable or disabled dates inside a range, but not at either endpoint.
maximumDaysnumberMaximum inclusive length accepted in range mode.
fixedDate"start" | "end"Keeps one boundary fixed while range interactions update only the opposite boundary.
disabledbooleanfalseDisables navigation and selection.
readOnlybooleanfalseAllows focus/navigation but blocks selection.
initialFocusbooleanfalseFocuses the roving day after mount.
disableDaysOutsideCurrentViewbooleanfalsePrevents selecting outside days.
minValueCalendarDateValueEarliest selectable date and navigation boundary.
maxValueCalendarDateValueLatest selectable date and navigation boundary.
isDateDisabled(date) => booleanMarks dates disabled by application rules.
isDateUnavailable(date) => booleanMarks dates unavailable and invalid when externally selected.
nextPage(placeholder) => CalendarDateValueadd month(s)Custom next-page calculation.
prevPage(placeholder) => CalendarDateValuesubtract month(s)Custom previous-page calculation.
uiCalendarUiClasses for generated structural parts.

Emits

EventPayloadDescription
update:modelValuevalueFired after accepted selection changes.
update:placeholderdateFired after accepted visible-month changes.
value-change(value, details)Fires before selection changes. details.cancel() prevents update.
placeholder-change(date, details)Fires before page changes. details.cancel() prevents update.

Slots

SlotPropsDescription
previousdisabledPrevious control content.
headinglabel, monthsCurrent month or range heading.
nextdisabledNext control content.
weekdaydate, labelWeekday heading content.
daydate, label, selected, rangeStart, rangeEnd, inRange, highlighted, today, outside, disabled, unavailable, focusedDay button content and complete selection state.
footervalue, placeholderOptional content after grids.

Methods

MethodDescription
selectDate(date, event?)Selects or deselects an accepted date.
focusDate(date, reason?, event?)Moves roving focus and visible month when needed.
previousPage(event?)Shows previous page when within constraints.
nextPage(event?)Shows next page when within constraints.
placeholderRead-only computed visible placeholder.

UI Options

KeyDescription
rootCalendar root.
headerNavigation header.
previousPrevious-page button.
headingLive month heading.
nextNext-page button.
gridsGrid collection wrapper.
gridEach ARIA date grid.
gridHeadGrid heading group.
gridBodyGrid body group.
gridRowWeekday and date rows.
headCellWeekday heading cells.
cellDate grid cells.
cellTriggerInteractive day buttons.
footerOptional footer wrapper.

Styling Hooks

UI keyCSS classData attrs
rootakaza-calendar`data-akaza-state="empty
headerakaza-calendar-header
previousakaza-calendar-previousdata-akaza-disabled
headingakaza-calendar-heading
nextakaza-calendar-nextdata-akaza-disabled
gridsakaza-calendar-grids
gridakaza-calendar-griddata-akaza-month
gridHeadakaza-calendar-grid-head
gridBodyakaza-calendar-grid-body
gridRowakaza-calendar-grid-row
headCellakaza-calendar-head-cell
cellakaza-calendar-cellstate and granular day attrs
cellTriggerakaza-calendar-cell-trigger`data-akaza-state="available
footerakaza-calendar-footer

Plain class applies to the Calendar root. Use ui.cellTrigger for generated day buttons and data variants for state styling.

Keyboard

KeyBehavior
ArrowLeft / ArrowRightMove one day, direction-aware in RTL.
ArrowUp / ArrowDownMove one week.
Home / EndMove to start/end of current week.
PageUp / PageDownMove one month.
Shift + PageUp / Shift + PageDownMove one year.
Enter / SpaceSelect or deselect focused date.

Only one day is in the Tab sequence. Arrow navigation can cross month boundaries and updates v-model:placeholder before moving focus.