elements
Demos UI Feed Discuss Install
Style System
  • Style
  • Themes
  • Behaviors
Forms
  • Button
  • ButtonGroup
  • Checkbox
  • ColorPicker
  • Combobox
  • Field
  • FileUpload
  • Input
  • InputGroup
  • InputOTP
  • Label
  • NativeSelect
  • NumberInput
  • PasswordInput
  • RadioGroup
  • Select
  • Slider
  • Switch
  • TagsInput
  • Textarea
  • Toggle
  • ToggleGroup
Overlays
  • AlertDialog
  • Command
  • ContextMenu
  • Dialog
  • Drawer
  • DropdownMenu
  • HoverCard
  • Menubar
  • NavigationMenu
  • Popover
  • Sheet
  • Toast
  • Tooltip
Display
  • Accordion
  • Alert
  • AspectRatio
  • Avatar
  • Badge
  • Breadcrumb
  • Card
  • Collapsible
  • Empty
  • Item
  • Kbd
  • Pagination
  • Progress
  • Separator
  • Skeleton
  • Spinner
  • Table
  • Tabs
  • Tree
Layout
  • Carousel
  • Resizable
  • ScrollArea
  • Sidebar
Dates
  • Calendar
  • DatePicker
  • DateRangePicker
  • TimePicker
Data
  • AreaChart
  • BarChart
  • Chart
  • DataTable
  • Kanban
  • LineChart
  • PieChart
  • RadarChart
  • RadialChart
  • VirtualList
Chat
  • Attachment
  • Message
  • MessageList
  • PromptInput
Elements UI / Dates / Calendar

Calendar

A month grid for picking one date, several dates or a range, with keyboard navigation across months.

Single date

Click a day or move with the arrows and press Enter. The footer is a live region that announces the pick.

October 2026
SuMoTuWeThFrSa
You picked October 14, 2026.

value: 2026-10-14

Code
<Calendar value={state.date} footer={picked(state.date)}
          help="Arrow keys move between days. Enter picks one." />

Usage

import { Calendar } from "@elements/ui/calendar";

<Calendar value={form.due} />
<Calendar mode="multiple" values={form.days} max={5} />
<Calendar mode="range" from={trip.from} to={trip.to} numberOfMonths={2} />
<Calendar captionLayout="dropdown" startMonth="1930-01" disabled={{ after: today }} />

Examples

Required

required keeps a selection: the single date cannot be cleared, the last of several dates stays, and a range keeps its start.

October 2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa

value: 2026-10-20

values: 2026-10-08; range: 2026-10-19 to none

Code
<Calendar value={form.date} required />
<Calendar mode="multiple" values={form.days} required />
<Calendar mode="range" from={trip.from} to={trip.to} required />

Range

Two months side by side. The first click starts the range, the second ends it, in either order.

October 2026
SuMoTuWeThFrSa
November 2026
SuMoTuWeThFrSa

2026-10-12 to 2026-10-23

Code
<Calendar mode="range" from={trip.from} to={trip.to} numberOfMonths={2} />

Multiple

Toggle up to five days, never fewer than one. A sixth starts a new selection.

October 2026
SuMoTuWeThFrSa

2026-10-06, 2026-10-15

Code
<Calendar mode="multiple" values={state.days} min={1} max={5} />

Month and year dropdowns

For a date of birth: pick the year and month from selects instead of paging. The selects are native, so phones show their own pickers.

Jun1990
SuMoTuWeThFrSa
Oct2026
SuMoTuWeThFrSa
2026
SuMoTuWeThFrSa

value: 1990-06-15

Code
<Calendar captionLayout="dropdown" value={person.dob}
          startMonth="1930-01" endMonth="2026-12" reverseYears />
<Calendar captionLayout="dropdown-months" ... />
<Calendar captionLayout="dropdown-years" ... />

Custom select dropdowns

dropdownStyle=listbox draws the month and year dropdowns as the library's Select (a combobox with a listbox) instead of native selects.

Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
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
SuMoTuWeThFrSa
Pick a day.
Code
<Calendar captionLayout="dropdown" dropdownStyle="listbox" value={state.picked}
          footer={state.picked ? `Selected: ${formatDate(state.picked, "en-US")}` : "Pick a day."} />

Intercepting clicks

onDayClick with preventDefault replaces the pick: a double click selects the day, a single click clears it, and the footer shows the date. Enter still selects from the keyboard.

October 2026
SuMoTuWeThFrSa
Code
<Calendar value={state.picked} footer={dateString(state.picked)}
          onDayClick={(d, m, e) => onDay(state, d, e)} />

function onDay(state, iso, e) {
  e.preventDefault();                                     // skip the default pick
  state.picked = e.detail >= 2 || e.detail === 0 ? iso : "";  // double click (or Enter) picks; a click clears
}

Disabled and booked dates

Past days, weekends and booked days are disabled; the booked ones are marked through a custom modifier.

November 2026
SuMoTuWeThFrSa

value: none

Code
<Calendar value={state.booking}
          disabled={[{ before: today }, { dayOfWeek: [0, 6] }, BOOKED]}
          modifiers={{ booked: BOOKED }} />

/* page.css: strike the booked days */
.calendar-day[data-modifiers~="booked"] > .calendar-day-button { text-decoration: line-through; }

Range rules

A stay of 2 to 7 nights that cannot cross a weekend (excludeDisabled), and a range that restarts on the next click (resetOnSelect).

October 2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa

stay: none to none; reset: 2026-10-05 to 2026-10-09

Code
<!-- 2 to 7 nights, never across a weekend -->
<Calendar mode="range" from={stay.from} to={stay.to}
          min={2} max={7} disabled={{ dayOfWeek: [0, 6] }} excludeDisabled />

<!-- a click after a complete range starts a new one -->
<Calendar mode="range" from={r.from} to={r.to} resetOnSelect />

Week numbers

The locale's week numbers with six fixed weeks, ISO 8601 weeks, the US broadcast calendar, and Persian weeks counted from 1 Farvardin.

October 2026
SuMoTuWeThFrSa
40
41
42
43
44
45
January 2027
MoTuWeThFrSaSu
53
1
2
3
4
November 2026
MoTuWeThFrSaSu
44
45
46
47
48
1405 فروردین
شیدسچپج
1
2
3
4
5
Code
<Calendar showWeekNumber fixedWeeks />
<Calendar showWeekNumber isoWeek />
<Calendar showWeekNumber broadcastCalendar />
<!-- weeks of the calendar's own year: week 1 holds 1 Farvardin -->
<Calendar showWeekNumber calendar="persian" locale="fa-IR" dir="rtl" numerals="latn" />

Time zones

timeZone sets which day is today: in Kiritimati (UTC+14) and Pago Pago (UTC-11) it is never the same day. Dates are plain ISO days, so January 1900 in Kolkata, whose old offset has seconds, still starts on Monday.

November 2026
SuMoTuWeThFrSa
November 2026
SuMoTuWeThFrSa
January 1900
SuMoTuWeThFrSa
Code
<!-- today is the date in the zone: these two are a day apart -->
<Calendar timeZone="Pacific/Kiritimati" />
<Calendar timeZone="Pacific/Pago_Pago" />
<!-- dates are plain ISO days, so an old zone offset never shifts a day -->
<Calendar timeZone="Asia/Kolkata" defaultMonth="1900-01" />

Outside and hidden days

Without outside days the other months' cells stay empty (this one starts on Monday with weekStartsOn). hidden removes days entirely; the keyboard skips them.

October 2026
MoTuWeThFrSaSu
October 2026
SuMoTuWeThFrSa
Code
<Calendar showOutsideDays={false} weekStartsOn={1} />
<Calendar hidden={{ dayOfWeek: [0, 6] }} />

Several months

pagedNavigation moves two months at a time; reverseMonths shows the later month first.

October 2026
SuMoTuWeThFrSa
November 2026
SuMoTuWeThFrSa
November 2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa
Code
<Calendar mode="range" numberOfMonths={2} pagedNavigation />
<Calendar numberOfMonths={2} reverseMonths />

Navigation limits

startMonth and endMonth bound the buttons, the keyboard and the dropdowns; the older names fromMonth, toMonth, fromYear and toYear do too. disableNavigation keeps one month; hideNavigation removes the buttons.

October 2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa
Oct2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa
Oct2026
SuMoTuWeThFrSa
Code
<Calendar startMonth="2026-09" endMonth="2026-12" />
<Calendar disableNavigation />
<Calendar hideNavigation captionLayout="dropdown" />
<!-- the older bound names, still read: -->
<Calendar fromMonth="2026-09" toMonth="2026-12" />
<Calendar captionLayout="dropdown" fromYear={2024} toYear={2027} />

Navigation after the caption

navLayout=after puts both buttons after the caption, so the Tab order matches what you see.

Oct2026
SuMoTuWeThFrSa
Code
<Calendar navLayout="after" captionLayout="dropdown" />

Locales and right to left

Names, the first day of the week and week numbering come from Intl. Arabic reads right to left; Hindi uses Devanagari digits.

أكتوبر ٢٠٢٦
سحنثرخج
Oktober 2026
MoDiMiDoFrSaSo
40
41
42
43
44
अक्टूबर २०२६
रविसोममंबुधगुशुशनि
Code
<Calendar locale="ar-EG" dir="rtl" />
<Calendar locale="de-DE" showWeekNumber />
<Calendar locale="hi-IN" numerals="deva" />

Other calendars

calendar sets the Intl calendar the grid follows: Persian, Hijri, Buddhist, Ethiopic or Hebrew months, captions and day numbers, each in its language and with Latin digits (in English, Amharic for Ethiopic), and the dropdowns list the calendar's own months and years. The value stays a Gregorian ISO date.

۱۴۰۵ آبان
شیدسچپج
Aban 1405
SuMoTuWeThFrSa
Bahman1402
SuMoTuWeThFrSa

value: none

Code
<Calendar calendar="persian" locale="fa-IR" dir="rtl" value={state.date} />
<Calendar calendar="persian" locale="en-US" numerals="latn" value={state.date} />
<Calendar calendar="islamic-umalqura" locale="ar-SA" dir="rtl" numerals="arab" />
<Calendar calendar="buddhist" locale="th-TH" numerals="thai" />
<Calendar calendar="ethiopic" locale="am-ET" numerals="ethi" />   <!-- Ge'ez numerals; numerals="latn" for Latin -->
<Calendar calendar="hebrew" locale="he-IL" dir="rtl" />
<Calendar calendar="hebrew" locale="en-US" captionLayout="dropdown" />   <!-- Adar I and Adar II in a leap year -->

Presets

Quick picks beside the calendar set the date and show its month.

October 2026
SuMoTuWeThFrSa
Code
<Calendar value={state.date} month={state.month} />
<Button e:for={p of PRESETS} onclick={() => setPreset(state, p.days)}>{p.label}</Button>

Controlled month

month is two-way. Bind it to show a month from code, such as a Go to Today button.

December 2026
SuMoTuWeThFrSa
month: 2026-12-01
Code
<Calendar value={state.date} month={state.month} />
<Button onclick={() => state.month = today}>Go to Today</Button>

Cell size

Set --calendar-cell-size for larger cells; everything else follows.

October 2026
SuMoTuWeThFrSa

Larger cells

Code
<Calendar class="calendar-large" />

/* page.css */
.calendar-large { --calendar-cell-size: var(--control-height-lg); }

Day content

dayContent puts a second line in each day: here a nightly price, higher on weekends and none on outside days, in larger cells.

Oct2026
SuMoTuWeThFrSa
Code
<Calendar mode="range" from={trip.from} to={trip.to} captionLayout="dropdown"
          class="calendar-priced" dayContent={price} />

function price(date: ISODate, m: DayModifiers): string {
  if (m.outside) return "";
  let day = new Date(date + "T00:00:00").getDay();
  return day === 0 || day === 6 ? "$120" : "$100";
}

Events

Every interaction reports itself; the last five events are shown.

October 2026
SuMoTuWeThFrSa
Code
<Calendar value={state.date}
          onSelect={(date, m) => log(`select ${date}`)}
          onMonthChange={(month) => log(`month ${month}`)}
          onDayFocus={(date) => log(`focus ${date}`)} />

Matchers

Every kind of rule disabled at once: a date, a range, before, after, between, and a function.

October 2026
SuMoTuWeThFrSa
Code
<Calendar disabled={[
  "2026-10-20",                                // one date
  { from: "2026-10-05", to: "2026-10-07" },    // a range, ends included
  { before: "2026-10-03" },                    // before, not including
  { after: "2026-10-27" },                     // after, not including
  { before: "2026-10-24", after: "2026-10-21" }, // between
  (date) => date.endsWith("-13"),              // your own rule
]} />

Focus on show

autoFocus moves focus to the calendar when it appears, for a calendar revealed by an action.

Code
<Calendar e:if={state.open} autoFocus />

Display

mode none only reports clicks. hideWeekdays drops the header row, formatters change the caption, day and weekday text, and firstWeekContainsDate sets which week of January is week 1 (here the week holding January 1). role and label name the whole calendar.

October 2026
SuMoTuWeThFrSa
October 2026
2026-10
SMTWTFS
January 2027
MoTuWeThFrSaSu
1
2
3
4
5

clicked: none

Code
<Calendar mode="none" onDayClick={(date) => opened = date} />
<Calendar hideWeekdays />
<Calendar formatters={{
  caption: (month) => month.slice(0, 7),         // "2026-10"
  day: (date) => date.slice(8),                  // "01"
  weekday: (date, locale) => narrowWeekday(date, locale),
}} />
<Calendar showWeekNumber weekStartsOn={1} firstWeekContainsDate={1}
          role="group" label="Week planner" />

Change events

onValuesChange and onRangeChange report the new selection after each pick.

October 2026
SuMoTuWeThFrSa
October 2026
SuMoTuWeThFrSa

no change yet

Code
<Calendar mode="multiple" values={days}
          onValuesChange={(values) => save(values)} />
<Calendar mode="range" from={trip.from} to={trip.to}
          onRangeChange={(range) => save(range)} />

Year buttons

yearButtons adds previous and next year buttons beside the month buttons, as the WAI-ARIA date picker dialog has. They respect startMonth and endMonth.

October 2026
SuMoTuWeThFrSa

no year move yet

Code
<Calendar value={form.date} yearButtons
          onPrevYearClick={(m) => log(m)} onNextYearClick={(m) => log(m)} />

Custom selections

mode none with onDayClick and modifiers builds any selection: a whole week, a rolling seven day window or a full month. weekNumberButtons makes each week number a button; onWeekNumberClick selects that week.

October 2026
SuMoTuWeThFrSa

none

Code
<Calendar mode="none" showWeekNumber weekNumberButtons
          modifiers={{ selected: { from, to }, rangeStart: from, rangeEnd: to,
                       rangeMiddle: { after: from, before: to } }}
          onDayClick={(date) => pick(date)}
          onWeekNumberClick={(week, dates) => pickWeek(dates)} />

Translated labels

labels sets every accessible name and formatters every visible text, here in Spanish: the navigation, the dropdowns, the week numbers, the weekday headers, the days and the grid.

octubre'26
#lunmarmiéjueviesábdom
S40
S41
S42
S43
S44
Code
<Calendar locale="es" captionLayout="dropdown" showWeekNumber yearButtons navLayout="after"
          labels={{ previous: "Ir al mes anterior", next: "Ir al mes siguiente",
                    weekday: (date) => ..., day: (date, m) => ..., grid: (month) => ... }}
          formatters={{ weekNumberHeader: () => "#", weekNumber: (n) => `S${n}`,
                        monthDropdown: (m) => ..., yearDropdown: (y) => ... }} />

Animated months

animate slides the weeks toward the direction of travel; reduced motion turns it off.

October 2026
SuMoTuWeThFrSa
Code
<Calendar animate />
Reference Calendar in the manual Every part, attribute, slot, key and data attribute.
Prev SidebarNext DatePicker
Product
Manual Pricing Compare
Company
History Contact
Legal
Privacy Policy Acceptable Use Terms License Agreement
© Elements Dev Corporation 2026