Month
A month grid with multi-day bars and a "+N more" popover.
Try it in the live demo: pick Month in the view switcher.
createMonthView shows every week that touches the month, starting on weekRules.firstDay. Most
months need five rows; some need four or six.
Add it
Copy registry/month-view into your app (it uses shadcn's popover), along with the theme and
registry/calendar, as described in UI source. Then
register the view:
import { createMonthView } from "@/components/calendar/month-view/month-view";
export const Calendar = createCalendar<Meeting>()({
eventAdapter,
views: [createMonthView<Meeting>({ getEventColor: (meeting) => meeting.color })],
});The view is named month. Previous and Next move by one month.
Anatomy
MonthView renders the parts below. The view builds them itself from its projection, so they're not
parts you compose: to change one, edit it in the registry/month-view files you copied. The tree
shows where each part lives.
- Month view
MonthView - Weekday header
- Week row
MonthWeekRow - Day outside the month
inMonth - Today
aria-current="date" - Day cell
data-date - Date number
DateButton - Event chip
MonthEvent - Multi-day bar
bar - Continuation
endsAfter - +N more
MoreEvents
MonthView month-view.tsx
├── Weekday header
└── MonthWeekRow month-week.tsx, one per week
├── Day cell data-date
│ └── DateButton aria-current="date" on today
├── MonthEvent month-event.tsx
└── MoreEvents "+N more" and its popoverA bar is a MonthEvent with bar set. startsBefore and endsAfter mark the ends that continue
into another week, and days with inMonth: false are muted.
Options
Prop
Type
What it shows
- Days: weekday names across the top. Days outside the month are muted, today's date sits in a filled circle, and the first of each month reads "Oct 1".
- Multi-day and all-day events are bars across the days they cover. A bar that continues into the previous or next week ends with a chevron. Timed bars start with their start time.
- Single-day timed events show a colored dot, the start time, and the title. On narrow screens they become small tinted blocks.
- "+N more": rows grow to fill the available height, at least 80px each, and show as many events as fit. The rest collapse into "+N more", which opens a popover with the day's events.
- Clicking a date number opens that day in the view named
day. Register a day view so the click has somewhere to go. - Editing: with
onEventChangeonRoot, drag an event to another day (timed events keep their time), and drag across days to create an all-day event. See Interactions. - Changes being saved: events with a pending change get
aria-busyand render at 60% opacity.
Keyboard
Tab into the preview above, or click a day, and try these keys. The Accessibility page has the full map and what a screen reader hears.
| Focus | Key | Effect |
|---|---|---|
| A day | ← → | Previous or next day |
| A day | ↑ ↓ | Same weekday a week earlier or later |
| A day | Home, End | First or last day of the week |
| A day | Page Up, Page Down | Same day in the previous or next month |
| A day | Enter | Go into the day's events. ↑ ↓ walk them, and Escape goes back to the day |
| A day | Shift+Enter | Open the day in the day view |
| A day | Space | Start an all-day event there. Arrow keys extend it, and Enter saves it |
| An event | Space | Pick it up. Arrow keys move it by days, Enter saves, and Escape cancels |
| An event | Shift+Space | Pick up its end to resize it by days. Shift+Space again switches to its start |
Events behind "+N more" are reachable too: while focus is on one, its week shows every event.
Markup for interactions
The view connects the pointer layer for you. If you change its markup, keep the attributes it reads:
each day cell holds an element with data-date underneath its events, and each event is a
<button> with data-event-id. The events sit beside that element, not inside it, so a drag over
a bar still finds the day under the pointer.
Last updated on