Browse docs
Browse docs
Calendar is a standalone, inline month grid for picking a single date. It is
a primitive — always visible, no popup, no form-bridge or RBAC integration.
For a form-bound date field use DatePicker,
which pairs a trigger with a Calendar popover.
Dates are plain ISO YYYY-MM-DD strings throughout — no time, no time zone. The
month grid, keyboard navigation, and localization come from the headless
@dashforge/calendar-core engine; this
Calendar is the Tailwind presentation layer over it — the same engine the
MUI @dashforge/ui calendar renders.
import { Calendar } from '@dashforge/tw';
<Calendar
defaultValue="2026-05-20"
onChange={(iso) => console.log(iso)}
/>A basic uncontrolled calendar — defaultValue seeds the selection, the grid
manages it from there.
import { Calendar } from '@dashforge/tw';
<Calendar defaultValue="2026-05-20" />minDate / maxDate bound the selectable range; disabledDates blocks
specific days. isDateDisabled (a predicate) covers rule-based cases such as
"weekdays only".
import { Calendar } from '@dashforge/tw';
<Calendar
defaultValue="2026-05-15"
minDate="2026-05-04"
maxDate="2026-05-29"
disabledDates={['2026-05-12', '2026-05-13', '2026-05-20']}
/>weekStartDay sets the first column (0 = Sunday, 1 = Monday). locale is
a BCP-47 tag driving the month and weekday names.
import { Calendar } from '@dashforge/tw';
<Calendar defaultValue="2026-05-20" weekStartDay={1} locale="en-GB" />Pass value + onChange to own the selection in your own state.
import { useState } from 'react';
import { Calendar, Stack, Typography } from '@dashforge/tw';
function ControlledCalendar() {
const [date, setDate] = useState<string | null>('2026-05-20');
return (
<Stack gap={2} sx="items-center">
<Calendar value={date} onChange={setDate} />
<Typography variant="caption" sx="text-neutral-500">
Selected: {date ?? '—'}
</Typography>
</Stack>
);
}Configure <Calendar> defaults application-wide.
import { patchTheme } from '@dashforge/tw-theme';
patchTheme({
components: {
Calendar: {
defaults: { weekStartDay: 1, locale: 'en-GB' },
},
},
});Configurable axes (CalendarVariantProps):
| Axis | Type | Notes |
|---|---|---|
weekStartDay | WeekDay (0 | 1 | … | 6) | First-column weekday (0 = Sunday, 1 = Monday). |
locale | string | BCP-47 locale for month + weekday names. |
Non-visual axes (value, defaultValue, onChange, month, year, minDate, maxDate, disabledDates, isDateDisabled, today, disabled, autoFocus) are per-instance — data and behavior — not theme-configurable.
Precedence chain (lowest → highest):
weekStartDay: 0, locale: 'en-US').theme.components.Calendar.defaults (application-wide).<Calendar weekStartDay={1} locale="fr-FR" />) — wins over theme.sx — appended to the root and wins over conflicting classes via tailwind-merge.TypeScript: theme.components.Calendar.defaults autocompletes to the two axes above.
Reactivity: useComponentDefaults('Calendar') subscribes to the theme store — patchTheme re-renders every mounted instance inheriting the changed axis.
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | — | Accessible label for the grid. Default "Calendar". |
autoFocus | boolean | — | Moves DOM focus to the active day cell on mount. |
defaultMonth | number | — | Initial displayed month (1-indexed, uncontrolled). |
defaultValue | ISODate | null | — | Initial selected date (uncontrolled). |
defaultYear | number | — | Initial displayed year (uncontrolled). |
disabled | boolean | — | Disables the whole calendar. |
disabledDates | readonly ISODate[] | — | Explicit list of disabled dates. |
isDateDisabled | (date: ISODate) => boolean | — | Predicate marking arbitrary dates disabled. |
locale | string | — | BCP-47 locale for month/weekday names. Default "en-US". |
maxDate | ISODate | — | Latest selectable date (inclusive). |
minDate | ISODate | — | Earliest selectable date (inclusive). |
month | number | — | Controlled displayed month (1-indexed). Pair with year. |
onChange | (value: ISODate) => void | — | Fired when a day is chosen. |
onMonthChange | (month: number, year: number) => void | — | Fired when the displayed month changes. |
showSiblingDays | boolean | true | Render the leading and trailing cells that belong to the neighbouring months. Default true, which is the convention React Day Picker, MUI's DateCalendar and the native pickers all follow.Set false and those cells become empty placeholders that keep the 7-column geometry. Worth doing for any form where "a day of the next month" is not a distinct concept, because a muted-but-selectable day reads as unavailable at a glance. See README-BUG § BUG 31. |
slotProps | CalendarSlotProps | — | Per-slot className overrides. |
sx | string | — | Root-level Tailwind class override. |
testId | string | — | Test id applied to the root element. |
today | ISODate | — | Overrides "today" (ISO YYYY-MM-DD), mainly for testing. |
value | ISODate | null | — | Selected date (controlled). |
weekStartDay | WeekDay | — | Weekday of the grid's first column (0 = Sunday). Default 0. |
year | number | — | Controlled displayed year. Pair with month. |
ISODate is a YYYY-MM-DD string; WeekDay is 0 \| 1 \| … \| 6. Both come
from @dashforge/calendar-core.
<Calendar> is a compound component with 4 named slots. Each slot accepts a { className?: string } override via slotProps:
| Slot | Purpose |
|---|---|
root | Outer calendar container (bordered surface). |
header | Top row with prev/month-year/next navigation. |
grid | The 7-column day grid wrapper. |
day | Every day-cell button (applies to all 42 cells uniformly). |
<Calendar
defaultValue="2026-05-20"
sx="rounded-xl shadow-md"
slotProps={{
header: { className: 'font-semibold' },
day: { className: 'font-medium' },
}}
/>Use sx for a root-level class override; use slotProps for a specific inner region (e.g. day hover color, header typography).
role="grid" with row /
columnheader / gridcell structure.Enter / Space selects.aria-current="date"; the selected day carries
aria-pressed.Calendar has no name, no validation, no RBAC.
It does not register with DashForm. Use DatePicker for form integration.YYYY-MM-DD only. No time component and no time zone — a stored date
never drifts across zones or DST.@dashforge/calendar-core; the same engine
powers the MUI Calendar in @dashforge/ui.dashforgePreset() — no dark: variants, so dark mode follows the CSS-var
swap automatically.