Skip to content

Components · Date, time and scheduling

CalendarMonthView

A month grid of days with events, "+N more" popovers and arrow-key movement.

@rookcom/monarch-react/schedulingComponent

Month with events

Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

Usage examples

Product-owned navigation

Narrow (dot summary)

Purpose

Shows a month at a glance: which days are busy, what is on them, and a way into each day and event. Needed by: calendar, people, stride.

Use when

  • The month view of a calendar (bookings, interviews, releases, shifts).
  • Planning views where users pick or open a day (attendance, due dates on a board).

Avoid when

  • The user needs times and overlaps: use TimeGrid.
  • Picking a single date in a form: use DatePicker or MiniMonth.
  • A long list of upcoming items: use AgendaList.

Design principles

  • A real grid (role="grid"): weekday column headers, one row per week, arrow keys between days.
  • Month navigation belongs to the product: it passes month and changes it from its own toolbar or from onNavigate.
  • Busy days stay calm: at most maxEvents lines per cell, the rest behind "+N more".
  • Today, outside-month days and hatched days are distinguished by shape and text as well as colour.
  • Phones get the same grid with dots instead of chips, not a different component.

API reference

import { CalendarMonthView } from '@rookcom/monarch-react/scheduling';
ExportKindRenderingRoot class
CalendarMonthViewcomponentclient.mn-calendar-month-view

Renders a <div> (ref) holding the role="grid" element. Days are ISO dates ("2026-11-04"); events are CalendarEvent objects read in timeZone.

The focus stop can be controlled (focusedDay, onFocusedDayChange) or uncontrolled (defaultFocusedDay); it defaults to today when in the month, else the 1st.

The overflow list uses Monarch Popover (Radix), labelled by the full date.

Pass timeZone, locale, weekStartsOn and today for identical server and client output; without today the clock is read after mounting.

Generated from the TypeScript declarations of packages/react/src/components/scheduling/calendar-month-view.

Properties

CalendarMonthView

A month of days with their events: weekday header, today and outside-month days marked, a "+N more" popover for busy days, arrow-key movement between days, and callbacks for opening a day or an event. Below about 36rem of container width, cells show a dot summary that opens the day's list.

PropTypeDescription
month requiredstringAny day in the month to show, `'2026-11-04'`. The product owns month navigation.
classNamestring
dayState((day: string) => CalendarMonthViewDayState | undefined)Decorate days: hatching and a short badge.
defaultFocusedDaystringInitial focus stop (uncontrolled). Default: today when it is in the month, else the 1st.
eventsreadonly CalendarEvent[]
fixedWeeksbooleanAlways show six weeks, so the height never jumps between months. Default `true`.
focusedDaystringThe day with the keyboard focus stop (controlled).
hourCycleHourCycleForce a 12- or 24-hour clock (`'12'` or `'24'`, as in the date pickers); by default the locale decides.
idstring
labelsCalendarMonthViewLabels
localestringBCP 47 locale for weekday, date and time labels.
maxEventsnumberEvents listed in a cell before the "+N more" button. Default 3.
onDayClick((day: string) => void)Called when a day's date is activated (click, Enter or Space).
onEventClick((event: CalendarEvent) => void)
onFocusedDayChange((day: string) => void)
onNavigate((day: string) => void)Called when keyboard focus moves to a day in another month (arrows into or past the outside days, Page Up / Page Down), with that day. Change `month` to follow it; the day keeps focus. Without it, focus moves only within the shown weeks.
renderEvent((event: CalendarEvent, chip: ReactElement) => ReactNode)Wrap or replace each chip, e.g. in a PopoverTrigger. Keep one focusable element.
timeZonestringIANA zone events are placed in. Defaults to the runtime's; pass it for server rendering.
todaystring | nullToday, `'2026-11-04'`. Inject it for server rendering and tests; when left out it is read from the clock (in `timeZone`) after mounting. `null` marks no day.
weekStartsOnWeekdayFirst column: 0 = Sunday … 6 = Saturday, as in MiniMonth and the date pickers. Default: the locale's week start.

Variants

PropValuesDescription
weekStartsOn'0''…''6'First column, 0 = Sunday … 6 = Saturday (the convention MiniMonth and the date pickers use); defaults to the locale’s week start (Intl week info), else Monday.
fixedWeeks'true''false'true (default) always shows six weeks so the height is stable; false shows only the weeks the month needs.
maxEvents'number'Lines per cell including the "+N more" button. Default 3.

States

StateExposed asBehaviour
Todaydata-today, aria-current="date"Accent-filled date and ", today" in the day’s name.
Outside monthdata-outsideSubtle background and muted date.
Hatcheddata-hatchedFrom dayState: time off or closures, with an optional badge in the name.
Focus stopdata-focusedThe one day in the tab order; its events and "+N more" are tabbable.
Overflow openaria-expanded="true"The day’s full list in a popover.

Interaction behaviour

  • Activating a date calls onDayClick(day); activating an event calls onEventClick(event) or follows its href.
  • "+N more" opens a popover with all of the day’s events; Escape or an outside click closes it and focus returns to the button.
  • Multi-day events appear in each day they touch; all-day events come first.
  • renderEvent wraps each chip (for example in the product’s event popover).

Keyboard support

KeysAction
TabMoves to the focused day’s date, then its events and "+N more", then out of the grid.
ArrowLeft / ArrowRightPrevious or next day (mirrored in right-to-left).
ArrowUp / ArrowDownSame weekday in the previous or next week.
Home / EndFirst or last day of the week.
Page Up / Page DownSame day in the previous or next month (Shift: year), through onNavigate.
Enter / SpaceActivates the date (onDayClick).

Moving into another month calls onNavigate(day); the product changes month and focus stays on that day. Without onNavigate, focus moves only within the shown weeks.

Accessibility

Role
grid, row, columnheader, gridcell
Accessible name
The grid is named by aria-label (default: the month and year). Each date is named "Wednesday, 4 November 2026, 5 events, today".
  • Column headers carry full weekday names for assistive technology and short ones on screen.
  • Roving tabindex: one day is in the tab order, so a month is not 42 tab stops.
  • The overflow popover is a dialog named by the full date.
  • Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/calendar-month-view.

Responsive behaviour

  • A container query switches cells below 36rem to a dot summary (up to three dots and "+N"); the summary button opens the day’s list.
  • The grid always fits its container (seven minmax(0, 1fr) columns); there is no horizontal scrolling at 320px.
  • Cell height comes from --mn-calendar-month-view-cell (7rem) and --mn-calendar-month-view-cell-narrow (3.5rem).

Theme support

  • Surfaces, borders and text are Monarch tokens; light, dark and more-contrast themes apply automatically.
  • Today uses --mn-color-accent-solid with --mn-color-accent-on-solid, so it follows the product accent.
  • Event colours only tint dots and chips (see EventChip).

Do and don't

Do

  • Keep month in your router or state and change it from onNavigate so keyboard users can leave the month.
  • Pass the user’s week start and time zone from their settings.
  • Use dayState for time off and holidays rather than fake all-day events.

Don't

  • Do not raise maxEvents to fit everything; the popover is the overflow.
  • Do not put controls other than events inside cells; open the day for detailed actions.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsEventChip, Popover

Testing coverage

  • Grid semantics: 7 rows, column headers with full names, 42 cells, outside days, today with aria-current.
  • Week start and only-needed weeks.
  • All-day first, capped cells, "+N more" popover with every event, event callback, Escape returns focus.
  • Day click callback.
  • Arrow keys, Home/End, Enter; one tab stop; focus stops at the edge without onNavigate.
  • Focused day’s events are tabbable, others are not.
  • Page Up/Down call onNavigate and focus follows the new month.
  • dayState hatching and badge; right-to-left arrows.
  • today={null}, clock fallback; no axe violations; renders on the server without the clock.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/calendar-month-view; Open overflow popover: overlays/calendar-month-view-more-light; Cross-browser axe: /components/calendar-month-view

Tests: packages/react/src/components/scheduling/calendar-month-view