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.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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).
| M | T | W | T | F | S | S |
|---|---|---|---|---|---|---|
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.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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.
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
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.
November 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
December 2026
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
const [selectedRange, setSelectedRange] = React.useState();
return (
<Calendar hasTodayButton={false} showOutsideDays={false} isDualMonthMode mode="range" onValueChange={setSelectedRange} value={selectedRange} />
);
API Reference
Calendar
| Prop | Default | Description |
|---|---|---|
copyLabels? | _ | objectCalendar copy labels for accessibility, tooltips and localization. Use this to provide translated or custom text for various calendar elements. |
| _ |
|
| _ |
|
copyLabels.goTodayLabel? | Today | stringAria label and tooltip content for the button that navigates to today. |
copyLabels.nextMonthLabel? | Next month | stringAria label and tooltip content for the button that navigates to the next month. |
copyLabels.nextYearLabel? | Next year | stringAria label and tooltip content for the button that navigates to the next year. |
copyLabels.previousMonthLabel? | Previous month | stringAria label and tooltip content for the button that navigates to the previous month. |
copyLabels.previousYearLabel? | Previous year | stringAria label and tooltip content for the button that navigates to the previous year. |
copyLabels.rangeStartLabel? | Range start | stringScreen reader label for a range's start date ( mode="range"). |
copyLabels.rangeEndLabel? | Range end | stringScreen reader label for a range's end date ( mode="range"). |
copyLabels.rangeMiddleLabel? | In selected range | stringScreen reader label for a date inside the range ( mode="range"). |
hasMonthPicker? | true | booleanDetermines whether Calendar has month grid or not. |
| _ |
If it returns |
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? | _ | DateThe maximum allowed date that a user may select. |
minValue? | _ | DateThe minimum allowed date that a user may select. |
mode? | single | 'single' | 'range'Whether the calendar selects a single Date or a DateRange ({ from, to }). |
| _ |
Not called for dates outside |
onValueChange? | _ | functionHandler called when the value changes, with the selected Date | undefined (single mode) or DateRange | undefined (range mode). |
onVisibleMonthChange? | _ | functionHandler that is called when the currently visible month changes. |
onVisibleYearChange? | _ | functionHandler that is called when the currently visible year changes. |
size? | standard | 'small' | 'standard'The size of the calendar. |
tzModifier? | Intl.DateTimeFormat().resolvedOptions().timeZone | stringThe 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 | DateRangeThe current value (controlled). A Date in single mode (the default).A DateRange ({ from, to }) in range mode. |
visibleMonth? | _ | DateControls the currently focused date within the calendar. |
weekStartsOn? | _ | 0 | 1 | 2 | 3 | 4 | 5 | 6The index of the first day of the week. Accepts a value from 0 (Sunday) to 6 (Saturday).
Overrides the locale's default setting. |
hasTodayButton? | true | booleanShow/Hide the today button in the calendar. |
isDualMonthMode? | false | booleanRenders 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? | true | booleanShow/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
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
<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 Parts | Description |
|---|---|
| caption | The main caption area containing the month, year and navigation buttons. |
| captionButton | The button for navigating to the previous/next month or year |
| captionButtonLabel | The label inside a captionButton |
| captionLabel | The label that displays the name of the month in the caption |
| captionYear | The element displaying the year in the caption |
| day | A single day cell within the month view |
| dayButton | The button inside a day cell |
| month | A single month within the calendar |
| monthGrid | The container holding all the month cells in the month view |
| monthCell | Represents a single month cell in the month picker view |
| monthCellText | The text inside a month cell |
| months | The container holding all rendered months |
| root | The root element of the calendar |
| week | A row within the month view, containing a week's worth of day cells |
| weekday | The cell containing each day's name in the week header |
| weekdays | The row containing the day names header |
| weeks | The container holding all week rows |