Calendar
calendarDisplays a navigable day, month, or year calendar with limits and disabled-date rules.
Usage
Basic usage
Resident calendar panel, without trigger or floating layer - if you want "input box + pop-up layer", use DatePicker, which is the panel inside. The external value is ISO date string YYYY-MM-DD.
<Calendar defaultValue="2026-06-08" />Select month / select year
picker determines the particle size and value shape: month → YYYY-MM, year → YYYY. The panel title can be clicked to scroll up to the month/year view layer by layer.
<Calendar picker="month" defaultValue="2026-06" />
<Calendar picker="year" defaultValue="2026" />Limited range + disabled weekends
minDate / maxDate frame the optional range, and disabledDate further prohibits selection on a daily basis.
<Calendar
defaultValue="2026-06-10"
minDate="2026-06-01"
maxDate="2026-06-30"
disabledDate={(iso) => {
const day = new Date(iso + "T00:00:00").getDay();
return day === 0 || day === 6;
}}
/>Specify the initial month
defaultMonth only determines which screen the panel stops on, regardless of the selected value - suitable for "no value but want to start viewing from a certain month".
<Calendar defaultMonth="2026-09-01" />Disabled / Read Only
disabled even stops turning pages; readOnly can turn pages but cannot select.
<Calendar defaultValue="2026-06-08" disabled />
<Calendar defaultValue="2026-06-08" readOnly />When to use
Use Calendar when the month view is an always-visible part of the interface, such as date navigation in a dashboard sidebar or a date-selection panel on a booking page. It has no trigger or popup.
For an input that opens a calendar on click, use DatePicker; its popup renders this component. Both share the same drill-down and disabled-date behavior. Use DateRangePicker for a range, or DateTimePicker when the user also selects a time.
Before 0.15.0, this component bridged MUI XDateCalendarand required four optional peer dependencies plusMuiBridgeProvider. It is now a dependency-free HulianUI implementation and works without that setup.
Import
import { Calendar } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| value | string | null | — | Controlled value. Its shape follows picker: "YYYY-MM-DD", "YYYY-MM", or "YYYY". |
| defaultValue | string | null | — | Initial value when uncontrolled, with the same shape as value. |
| picker | "date" | "month" | "year" | "date" | Selection granularity; also determines the value shape and initial panel level. |
| defaultMonth | string | Follows value | Initial visible month as any parseable date string, independent of the selected value. Internal navigation takes over afterward. |
| minDate | string | — | Earliest selectable date as any parseable date string; normalized internally. |
| maxDate | string | — | Latest selectable date. |
| disabledDate | (isoDate: string) => boolean | — | Determines whether a date is disabled. The argument is always "YYYY-MM-DD"; month/year pickers pass the first day of that month/year. |
| showToday | boolean | true | Shows the locale-aware Today, This month, or This year shortcut for the active picker. |
| disabled | boolean | false | Disables the entire panel, including navigation. |
| readOnly | boolean | false | Allows navigation but prevents selection. |
| aria-label | string | From ConfigProvider | Accessible name for the panel. An explicit value takes precedence. |
| className | string | — | Additional class name for the outer panel container. |
Events
| Event | Type | Description |
|---|---|---|
| onValueChange | (value: string) => void | Called only when the user selects at the configured picker level. Drilling between panel levels does not trigger it. The panel has no clear action, so it never returns null. |
Usage guidelines
- Values are fixed-width strings, not `Date` objects. With
"YYYY-MM-DD", lexical order matches chronological order, so ranges can be compared as strings. This also avoids timezone boundary errors such asnew Date("2026-06-08").toISOString()producing the previous date in UTC+8. Convert explicitly if your application needs aDate. onValueChangereceivesstring, notstring | null, because the panel has no clear action. Use DatePicker when the user needs a clearable trigger.- Drilling down does not emit a value. With
picker="date", opening the month view and choosing September only moves to that month; the value changes only after a day cell is selected. Tests should not treat choosing the month as a completed date selection. - At
dategranularity,disabledDateruns once per visible day, typically 42 times per panel. Keep it a pure, inexpensive calculation: do not issue requests or repeatedly allocate heavy objects. Month and year pickers call it only for the first day of each month/year, so the rule is necessarily coarser; use the date picker for day-level precision. readOnlystill allows navigation, whiledisabledalso disables the navigation controls.- Month titles, weekday and month names, navigation labels, and shortcut copy follow the nearest
ConfigProviderlocale (zhCNby default, orenUS). ISO values and selection rules do not change with locale. - The panel has a fixed width of
15.75rem(7 columns × 2.25rem) and does not shrink responsively. Scale it explicitly if it must fit in a narrower container.
Related
DatePicker · DateRangePicker · DateTimePicker · TimePicker · TimeField · Scheduler
Playground
<Calendar
value={date}
onValueChange={setDate}
/>