Skip to content
v2.5.0
English
Русский
Documentation

Calendar

Calendar is an advanced interactive compound component for coordinated date selection across day, month, and year views.
SunMonTueWedThuFriSat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
January
February
March
April
May
June
July
August
September
October
November
December
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
calendar.tsx · 39 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,
Browser support
Newly available 2024
  • Chrome 125
  • Edge 125
  • Firefox 128
  • Safari 18
  • Safari on iOS 18
Bundle size
Tested in
144 tests

Summary

Calendar is an advanced interactive compound component for coordinated date selection across day, month, and year views. It is the consumer-facing orchestration layer on top of the lower-level picker family.

Imports

import {
  Calendar,
  type CalendarProps,
  type CalendarView,
  type CalendarSelection,
  type CalendarDatesDisabled,
} from '@vega-ui/react';

Exported Types

  • CalendarProps
  • CalendarView
  • CalendarSelection
  • CalendarDatesDisabled

Minimal Composition

en-US
ru-RU
ar-EG
he-IL
minimal-composition.tsx · 53 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Required Parts

  • Calendar: root state and date-selection model

Optional Parts

  • CalendarHeader, CalendarPrevButton, CalendarNextButton: navigation chrome; previous and next follow the current view
  • CalendarViewButtonGroup, CalendarViewButton: month and year view toggles
  • CalendarMonthLabel, CalendarYearLabel: labels of the visible month and year
  • CalendarContent: wrapper for the views
  • CalendarDays, CalendarMonths, CalendarYears: day, month, and year views
  • CalendarDay, CalendarMonth, CalendarYear: cell templates of the views, with useCalendarDayContext, useCalendarMonthContext, useCalendarYearContext

Composition Order

Typical composition:

  1. Calendar
  2. CalendarHeader with CalendarPrevButton, CalendarViewButtonGroup, CalendarNextButton
  3. CalendarContent
  4. CalendarDays, CalendarMonths, CalendarYears; only the current view is visible

Variants

  • Selection: single, multiple, range
  • View switching: day, month, year
  • Density: compact plus size variants
  • Composition depth: default views versus custom cell templates
  • Locale and calendar system: locale and calendar root props, defaulting to the runtime locale and the Gregorian calendar; see Dates And Calendar Systems

Edge Cases

  • Selection mode changes the value shape.
  • Disabled-date constraints can come from a value list or predicate logic.
  • Custom cell templates should be retested for keyboard flow, overflow days, and visible period changes.
  • value, defaultValue, min, max, month, and disabledDates are ISO YYYY-MM-DD strings in every calendar system; only the grid and labels follow calendar.
  • Non-Gregorian calendars come from createCalendarSystem; see Dates And Calendar Systems.

Common Mistakes

  • Forgetting that selection changes the expected value model.
  • Passing Date objects, timestamps, or display-calendar strings instead of ISO YYYY-MM-DD strings.
  • Reading the day number from the ISO date of a cell instead of label or fields.day; it is wrong in non-Gregorian calendars.
  • Combining min, max, and disabledDates without validating the resulting UX.

Examples

Basic

Basic: single date in a locale

The full composition: header with navigation and view switches, then the day, month, and year views. locale sets the month and weekday names and the first day of the week; defaultValue is an ISO day and also opens its month. The navigation buttons and the views have no built-in names, so they get aria-labels in the language of the calendar.

single-date-in-a-locale.tsx · 55 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Controlled/Stateful

Controlled/Stateful: value and visible month

value and month are both controlled, so a "Today" button can select today and scroll to it in one step. Navigating only calls onChangeMonth, which the readout below the calendar follows; the value changes only when a day is picked.

value-and-visible-month.tsx · 88 lines
import {
  Button,
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Controlled/Stateful: range with a summary

In range mode the value is a [start, end] tuple; '' marks a bound that is not picked yet. The bounds always come in order, start before end, whichever was picked first, so the summary can count nights without sorting.

en-US
ru-RU
ar-EG
he-IL
range-with-a-summary.tsx · 79 lines
import {
  Button,
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Controlled/Stateful: multiple dates with a limit

selection='multiple' makes the value an array of ISO days in the order they were picked. A controlled value can refuse a change: here onChangeValue ignores a sixth day, so the calendar keeps showing five.

multiple-dates.tsx · 78 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Form/Integration

Form/Integration: submit the date with a form

Calendar has no form value of its own and no required: keep the ISO day in state, submit it with a hidden input, and read it back with FormData. The empty check runs on submit, and its message is linked to the day grid with aria-describedby; the reset button clears the state through the form's reset event.

submit-the-date-with-a-form.tsx · 98 lines
import {
  Button,
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Layout/Overlay

Layout/Overlay: calendar in a popover

A date picker made of a trigger and a compact calendar in Popover. The popover is controlled, so it closes as soon as a day is picked; the trigger's accessible name keeps the field name next to the chosen date.

calendar-in-a-popover.tsx · 78 lines
import {
  Button,
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Error

Error: range across a blocked day

disabledDates stops a blocked day from being picked, but a range can still span it. Validate the range in the app, show the error only while the value is wrong, and link it to the day grid with aria-describedby.

range-across-a-blocked-day.tsx · 76 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Disabled

Disabled: whole calendar while saving

disabled turns off selection and navigation in every view, here while the new meeting date is being saved. The status next to the calendar says what is happening; a disabled calendar alone does not.

whole-calendar-while-saving.tsx · 78 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Disabled: booking window, weekends and holidays

Days before min and after max are disabled, and the previous / next buttons hide at those months. The disabledDates predicate closes weekends and a list of holidays; an array of ISO days works too when the list is all there is. The rule is written next to the calendar and linked with aria-describedby, because a disabled day does not say why.

booking-window-and-blocked-days.tsx · 69 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Edge

Edge: overflow days

includeOverflowDays fills the empty cells with days of the previous and next months. They are shown but cannot be selected; the day template styles them through inMonth.

overflow-days.tsx · 60 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Edge: min and max inside one month

When min and max fall in the same month, both previous and next buttons hide and only the days between them can be picked; keyboard focus starts on the first available day. The calendar does not move to min by itself: without defaultMonth it opens on the current month, even when that month is out of range.

min-and-max-inside-one-month.tsx · 60 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarViewButton,

Edge: 13-month year

With a calendar system from createCalendarSystem (see Calendar System Adapter), the month view follows that calendar. The Hebrew year 5784 is a leap year, so CalendarMonths shows 13 months, Adar I and Adar II included. The value is still an ISO day, as the readout shows.

13-month-year.tsx · 64 lines
import { createCalendarSystem } from '@vega-ui/i18n';
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonth,
  CalendarMonthLabel,
  CalendarMonths,
  CalendarNextButton,
  CalendarPrevButton,

Anatomy

Overview

Calendar owns the visible month, the current view, selection state, disabled-date rules, locale and calendar system, and keyboard coordination across the day, month, and year views.

Required Parts

Calendar

Root provider for the date-selection model.

  • Required: yes
  • Owns: view state, visible month, selection state, disabled-date logic, locale and calendar

Optional Parts

CalendarHeader

Header wrapper for navigation and view switching.

CalendarPrevButton / CalendarNextButton

Show the previous or next page of the current view: the previous or next month in the days, the previous or next page of years in the years. Hidden in the months view, and in the days when the previous or next month is outside min / max.

CalendarViewButtonGroup

Keeps the view buttons together between the previous and next buttons.

CalendarViewButton

view='month' or view='year' toggles that view; pressing it again goes back to the days. It is pressed (aria-pressed) while its view is shown.

CalendarMonthLabel / CalendarYearLabel

Visible month and year in the locale and calendar system of the root.

CalendarContent

Main content wrapper for the views.

CalendarDays

Day view. Its children are CalendarWeekdays and CalendarWeeks.

CalendarWeekdays

Row of weekday names in the locale of the root, starting on the first day of its week. Prop: format ('long' | 'short' | 'narrow', default 'short'). Its required children are the template of a weekday, usually <CalendarWeekday />; read the weekday with useCalendarWeekdayContext().

CalendarWeeks

Day grid, week by week, swipeable between months. Props: format, includeOverflowDays.

CalendarMonths

Month view: the months of the visible year, 12 or 13 depending on the calendar system. Props: cols, format.

CalendarYears

Year view: pages of years, swipeable. Prop: format.

CalendarDay / CalendarMonth / CalendarYear

Cell templates. Children of CalendarWeeks, CalendarMonths, and CalendarYears are required and rendered once per cell inside a cell context; usually <CalendarDay />, <CalendarMonth />, or <CalendarYear />, which shows the formatted label. Read the cell with useCalendarDayContext(), useCalendarMonthContext(), or useCalendarYearContext().

Composition Order

Typical composition:

  1. Calendar
  2. CalendarHeader with navigation buttons and view buttons
  3. CalendarContent
  4. CalendarDays, CalendarMonths, CalendarYears; only the current view is visible

Valid Composition Patterns

The month and year views are optional. A day-only calendar keeps CalendarDays alone in CalendarContent and shows the month and year as plain text between the navigation buttons; without CalendarMonths and CalendarYears there is nothing for a CalendarViewButton to open, so leave it out. The full composition is in Minimal Composition.

valid-composition-patterns.tsx · 39 lines
import {
  Calendar,
  CalendarContent,
  CalendarDay,
  CalendarDays,
  CalendarHeader,
  CalendarMonthLabel,
  CalendarNextButton,
  CalendarPrevButton,
  CalendarWeekday,
  CalendarWeekdays,
  CalendarWeeks,

Invalid Composition Patterns

View parts outside Calendar

The views and cell templates rely on root context. Moving them outside Calendar breaks coordination.

Custom cell templates without CalendarDay

A template must render CalendarDay (or CalendarMonth, CalendarYear) as the cell; content goes inside it. Without it the grid loses selection, focus, and disabled states.

Treating day, month, and year views as isolated widgets

They are coordinated views of the same calendar state model, not unrelated components.