Calendar

A calendar component that allows users to select a single date, or a date range.

Available from eds-core/1.11.0

Range selection (mode="range") available from eds-core/1.46.0

Quick Start

Installation
npm install @adaptavant/eds-core
Import
import { Calendar } from '@adaptavant/eds-core';
Added
1.11.0

Basic Usage​

The Calendar component accepts value and onValueChange props, allowing you to control its state.

You selected Fri Nov 06 2026.
const [selectedDate, setSelectedDate] = React.useState(new Date());

return (
	<Stack className="gap-4">
		<Calendar onValueChange={setSelectedDate} value={selectedDate} />
		<Text>
			{selectedDate
				? `You selected ${selectedDate.toDateString()}.`
				: 'Please pick a day.'}
		</Text>
	</Stack>
);

Size​

Customise the size of the Calendar via the size prop.

const [smallDate, setSmallDate] = React.useState();
const [standardDate, setStandardDate] = React.useState();

return (
	<Box className="flex gap-5">
		{/* Small */}
		<Calendar onValueChange={setSmallDate} size="small" value={smallDate} />
		{/* Standard */}
		<Calendar onValueChange={setStandardDate} value={standardDate} />
	</Box>
);

Unavailable days​

You can mark dates as unavailable using the isDateUnavailable prop, which takes a function that receives a date and returns true if that date is unavailable.

Unavailable dates are disabled by default. Pass onUnavailableDateClick to keep them selectable instead - it fires alongside onValueChange, so you can show an "unavailable" message elsewhere.

In this example, weekends and today are unavailable but still selectable.

Please pick a day.
function isWeekend(date) {
  const day = date.getDay();
  return day === 0 || day === 6;
}

function isToday(date) {
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  date.setHours(0, 0, 0, 0);
  return today.getTime() === date.getTime();
}

const [selectedDate, setSelectedDate] = React.useState();
const [status, setStatus] = React.useState('Please pick a day.');

return (
	<Stack className="gap-4">
		<Calendar
			isDateUnavailable={(date) => isWeekend(date) || isToday(date)}
			onUnavailableDateClick={(date) => setStatus(`You selected ${date.toDateString()}, but that date is unavailable.`)}
			onValueChange={(date) => {
				setSelectedDate(date);
				setStatus(`You selected ${date.toDateString()}.`);
			}}
			value={selectedDate}
		/>
		<Text>{status}</Text>
	</Stack>
);

First day of week​

Use the weekStartsOn prop to change the first day of the week. Accepts a value from 0 (Sunday) to 6 (Saturday).

const [selectedDate, setSelectedDate] = React.useState();

return (
	<Stack className="gap-4">
		<Calendar onValueChange={setSelectedDate} value={selectedDate} weekStartsOn={1} /> {/* Monday */}
	</Stack>
);

Min and Max value​

Use the minValue and maxValue props to define a specific date range for the calendar. This limits users to selecting dates only within the designated range and prevents navigation to months outside the specified months.

Out-of-range days are never clickable, and take precedence over isDateUnavailable.

Note: The below calendar restricts date selection to the current year using minValue and maxValue props. Users cannot navigate to months outside this range, ensuring the dates from other years are disabled.

Please pick a day.
const currentYear = new Date().getFullYear();
const minValue = new Date(currentYear, 0, 15); // January 15, current year
const maxValue = new Date(currentYear, 11, 15); // December 15, current year

const [selectedDate, setSelectedDate] = React.useState();
const [status, setStatus] = React.useState('Please pick a day.');

return (
	<Stack className="gap-4">
		<Calendar
			maxValue={maxValue}
			minValue={minValue}
			onValueChange={(date) => {
				setSelectedDate(date);
				setStatus(`You selected ${date.toDateString()}.`);
			}}
			value={selectedDate}
		/>
		<Text>{status}</Text>
	</Stack>
);

Without Today Button​

Set hasTodayButton to show/hide the today date button. By default, it is set to true.

Please pick a day.
const [selectedDate, setSelectedDate] = React.useState();

return (
<Stack className="gap-4">
	<Calendar onValueChange={setSelectedDate} value={selectedDate} hasTodayButton={false} />
	<Text>
		{selectedDate
		? `You selected ${selectedDate.toDateString()}.`
		: 'Please pick a day.'}
	</Text>
</Stack>
);

Time zones​

Use the tzModifier prop to configure the calendar to a specific timezone

You selected Fri Nov 06 2026.
const tzModifier = 'Asia/Tokyo';

const [selectedDate, setSelectedDate] = React.useState(new Date());

return (
	<Stack className="gap-2">
		<Calendar
			onValueChange={(date) => {
				if (date) {
					setSelectedDate(date);
				}
			}}
			tzModifier={tzModifier}
			value={selectedDate}
		/>
		<Text>
			{selectedDate
			? `You selected ${selectedDate.toDateString()}.`
			: 'Please pick a day.'}
		</Text>
	</Stack>
);

Range selection​

Set mode="range" to let users select a start and end date instead of a single date. In range mode, value and onValueChange work with a DateRange ({ from, to }) instead of a Date.

Please pick a start date.
const [range, setRange] = React.useState();

return (
	<Stack className="gap-4">
		<Calendar mode="range" onValueChange={setRange} value={range} />
		<Box className="text-body-14 px-3">
			{range?.from
				? `${range.from.toDateString()} - ${
						range.to ? range.to.toDateString() : '?'
					}`
				: 'Please pick a start date.'}
		</Box>
	</Stack>
);

Dual month mode​

Set isDualMonthMode on a range picker to render two months side by side.

const [selectedRange, setSelectedRange] = React.useState();

return (
<Calendar hasTodayButton={false} showOutsideDays={false} isDualMonthMode mode="range" onValueChange={setSelectedRange} value={selectedRange} />
);

API Reference​

Calendar

PropDefaultDescription
copyLabels?_object
Calendar copy labels for accessibility, tooltips and localization. Use this to provide translated or custom text for various calendar elements.

copyLabels.disabledDayLabel?

_

string
Tooltip content for the days that are not available.

⚠️ Deprecated, no longer rendered. The tooltip on disabled days was removed because it overlaid adjacent selectable cells.

copyLabels.disableMonthLabel?

_

string
Tooltip content for the months that are not available.

⚠️ Deprecated, no longer rendered. The tooltip on disabled months was removed because it overlaid adjacent selectable cells.

copyLabels.goTodayLabel?Todaystring
Aria label and tooltip content for the button that navigates to today.
copyLabels.nextMonthLabel?Next monthstring
Aria label and tooltip content for the button that navigates to the next month.
copyLabels.nextYearLabel?Next yearstring
Aria label and tooltip content for the button that navigates to the next year.
copyLabels.previousMonthLabel?Previous monthstring
Aria label and tooltip content for the button that navigates to the previous month.
copyLabels.previousYearLabel?Previous yearstring
Aria label and tooltip content for the button that navigates to the previous year.
copyLabels.rangeStartLabel?Range startstring
Screen reader label for a range's start date (mode="range").
copyLabels.rangeEndLabel?Range endstring
Screen reader label for a range's end date (mode="range").
copyLabels.rangeMiddleLabel?In selected rangestring
Screen reader label for a date inside the range (mode="range").
hasMonthPicker?trueboolean
Determines whether Calendar has month grid or not.

isDateUnavailable?

_

function
A callback function invoked for each date in the calendar.

If it returns true, then the corresponding date is marked as unavailable. Unavailable dates are disabled unless onUnavailableDateClick is also provided, which keeps them selectable.

localeCode?en'ar' | 'bg' | 'cs' | 'da' | 'de' | 'el' | 'en' | 'es' | 'et' | 'fi' | 'fr' | 'he' | 'hr' | 'hu' | 'is' | 'it' | 'ja' | 'ko' | 'lt' | 'lv' | 'mk' | 'nl' | 'no' | 'pl' | 'pt' | 'ro' | 'ru' | 'sl' | 'sq' | 'sr' | 'sv' | 'tr' | 'uk' | 'zh'
Locale code (ISO 639-1) for date localization.
maxValue?_Date
The maximum allowed date that a user may select.
minValue?_Date
The minimum allowed date that a user may select.
mode?single'single' | 'range'
Whether the calendar selects a single Date or a DateRange ({ from, to }).

onUnavailableDateClick?

_

function
Handler that is called when a user clicks or activates a date flagged by isDateUnavailable. Providing it keeps those dates selectable, so onValueChange fires as well. Not called for out-of-range dates.

Not called for dates outside minValue/maxValue, even when they are also unavailable - those are disabled and cannot be clicked.

onValueChange?_function
Handler called when the value changes, with the selected Date | undefined (single mode) or DateRange | undefined (range mode).
onVisibleMonthChange?_function
Handler that is called when the currently visible month changes.
onVisibleYearChange?_function
Handler that is called when the currently visible year changes.
size?standard'small' | 'standard'
The size of the calendar.
tzModifier?Intl.DateTimeFormat().resolvedOptions().timeZonestring
The timezone modifier for the calendar to consider the "today" date. When a consumer passes a valid timezone string, "today" (which was originally based on the system time) will be offset according to the specified timezone.
value?_Date | DateRange
The current value (controlled).
A Date in single mode (the default).
A DateRange ({ from, to }) in range mode.
visibleMonth?_Date
Controls the currently focused date within the calendar.
weekStartsOn?_0 | 1 | 2 | 3 | 4 | 5 | 6
The index of the first day of the week. Accepts a value from 0 (Sunday) to 6 (Saturday).
Overrides the locale's default setting.
hasTodayButton?trueboolean
Show/Hide the today button in the calendar.
isDualMonthMode?falseboolean
Renders two months side by side, paging both at once. Forces the previous button onto the leftmost caption and the next button onto the rightmost, and disables the month picker. Only applies to mode="range".
showOutsideDays?trueboolean
Show/Hide days from the previous and next months that fill out the current month's grid.

Style API​

Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.

Please refer to the Style API documentation for more insights.

Calendar parts​

<Stack className="gap-4">
	<Calendar
		classNames={{
			caption: 'border-2 bg-caution',
			captionButton: 'bg-neutral-tertiary rounded-none hover:scale-110',
			captionButtonLabel: 'font-mono',
			captionLabel: 'font-mono',
			captionYear: 'font-mono',
			monthGrid: 'border p-1',
			monthCell: 'hover:scale-125 bg-constant-black',
			monthCellText: 'font-mono text-constant-white',
			weekday: 'bg-positive-secondary',
			weekdays: 'p-2 m-2 bg-positive-hover',
			day: 'border',
			dayButton: 'hover:scale-125 bg-constant-black',
		}}
	/>
</Stack>
Stylable PartsDescription
captionThe main caption area containing the month, year and navigation buttons.
captionButtonThe button for navigating to the previous/next month or year
captionButtonLabelThe label inside a captionButton
captionLabelThe label that displays the name of the month in the caption
captionYearThe element displaying the year in the caption
dayA single day cell within the month view
dayButtonThe button inside a day cell
monthA single month within the calendar
monthGridThe container holding all the month cells in the month view
monthCellRepresents a single month cell in the month picker view
monthCellTextThe text inside a month cell
monthsThe container holding all rendered months
rootThe root element of the calendar
weekA row within the month view, containing a week's worth of day cells
weekdayThe cell containing each day's name in the week header
weekdaysThe row containing the day names header
weeksThe container holding all week rows