Calendar
Dependency-free month-grid date picker. The header can stay simple or become an interactive month/year chooser.
Basic
The default header is a plain, non-clickable month label.
- Follows the ARIA APG date-grid pattern: Arrow keys move by day, Home/End jump to the start/end of the week, and Page Up/Down move a month - only one date is a Tab stop at a time.
- The grid renders at a fixed 280px width regardless of viewport, which fits down to the smallest supported phone widths without its own responsive breakpoints.
Interactive month/year header
Set interactiveHeader to open a compact month and year chooser anchored inside the calendar. Years run from the current year back 100 years, respecting min/maxDate.
Min and max range
Days outside the allowed range are disabled.
Locale
locale takes a BCP 47 tag and drives the month and weekday names, and each day's accessible name, through Intl.
When to use
- The date grid should always be visible - inline scheduling, a booking page, a dashboard filter that's used often.
- As the anchored popup inside Date Picker, when a compact text-field trigger is what you actually want on the page.
When not to use
- A single date field in a form where screen space matters - use Date Picker so the grid only appears on demand.
- Time selection - pair it with Time Picker rather than extending Calendar itself.
Keyboard
Mobile behavior
- Fixed 280px grid width fits down to the smallest supported phone widths without a separate mobile layout.
- Day cells meet a 44px touch target even though the grid itself is compact.
Accessibility notes
- Follows the ARIA APG date-grid pattern with role="grid" and a roving tabindex - only the focused date is a Tab stop.
- Each day cell is named with its full date in the calendar's locale ("Saturday, March 14, 2026"). Today carries aria-current="date" and is marked with a dot as well as a color.
- SSR-safe "today": the initial render uses a UTC-normalized date so server and client markup match, then corrects to the visitor's local today after mount if they differ - never a hydration mismatch.
Common mistakes
- Passing a raw `new Date()` for `value`/`defaultValue` in a Server Component tree - use a UTC-normalized date (see the Basic example above) to avoid a timezone-dependent hydration mismatch.
- Setting minDate after maxDate (or vice versa) - the grid will show every day disabled with no explicit empty-state messaging.
Known limitations
- Single-month view only - no built-in multi-month or date-range selection.
Related components
Calendar props
Controlled selected date.
Uncontrolled initial date.
Dates outside this range render disabled.
Makes the month/year label open a compact chooser instead of a static label.
Locale used for month/weekday names.
API reference
Extra classes merged onto the calendar surface.
Starting selection when uncontrolled.
Makes the header month/year label an interactive chooser. Defaults to false, which keeps the simple non-clickable header.
BCP 47 tag driving the weekday and month names, via `Intl`.
Text appended to a marked day's accessible name ("Monday, May 20, 2024, has tasks"). Default "marked". Describe what the mark means.
Days to mark with a small dot (e.g. days with tasks): a list of dates matched by calendar day, or a predicate per visible day. Marked days get `markedDateLabel` in their accessible name.
Latest selectable date; days after it render disabled. Compared by calendar day, like `minDate`.
Earliest selectable date; days before it render disabled. Compared by calendar day, so `minDate={new Date()}` keeps today selectable.
Called with the picked date.
The date treated as "today" (highlight, `aria-current="date"`). Defaults to the visitor's local date after mount (SSR uses a UTC placeholder); pass it for deterministic output or tests.
Controlled selection. Pair with `onChange`; omit for uncontrolled.
Coding agents read this reference, with its examples, from the version your project has installed, through the Fuji UI MCP server. Set up your agent
