Skip to main content
@super-calendar/dom renders the calendar with real DOM elements. There is no React Native and no react-native-web in the tree: it builds on the same headless core as the native package and on Legend List’s DOM renderer. Reach for it when your app is web-only, or web-first, and you don’t want to pull a React Native runtime into the bundle. If you already ship a React Native app and want one codebase that also runs in the browser, use @super-calendar/native on react-native-web instead.

Install

react, react-dom, @legendapp/list, and date-fns are peer dependencies, so they resolve to the single copy your app already uses.

What it renders

Calendar mirrors the React Native component’s API across every view, including mode="schedule" (the Agenda list). You can also drop down to the individual views. The selection hook (useDateRange) and grid helpers (useMonthGrid, buildMonthGrid, layoutDayEvents) are re-exported from the core, so you import everything from one place. The month views take an optional events prop: pass it (even []) and the grid switches from the compact picker look to a calendar layout with event chips and a “+N more” overflow; omit it for the picker. TimeGrid supports businessHours shading, timeslots, a bounded hour window (minHour/maxHour, plus hideHours to drop the hour axis), showWeekNumber (with weekNumberPrefix), onPressCell, drag-to-create (onCreateEvent), and drag-to-move/resize (onDragEvent, which can return false to reject a drop). Creating on empty space is pointer-only (drag to sweep out a range); see Keyboard navigation for how focus moves.

The Calendar wrapper

The quickest start mirrors the native component: pick a mode and pass events.
Switch mode to "month" for a month grid with event chips, "schedule" for a day-grouped agenda list, or "day" / "3days" / "custom" (with numberOfDays) for the time grid. Custom rendering splits by view: renderTimeEvent for the time grid, renderMonthEvent for month chips, and renderScheduleEvent for agenda rows.

Schedule (agenda)

mode="schedule" (or the Agenda component directly) renders a scrolling list of events grouped under a date header per day, sorted by start. It does not window by date: it shows exactly the events you pass, so you control the range. Each row defaults to a time + title; override it with renderScheduleEvent.

A time grid and a range picker

TimeGrid and MonthList take an explicit height because they virtualize and scroll inside that box. Everything else (events, selection, week start) matches the native API, since both renderers share the core types.

Selection

Selection works as in the native Date selection guide: useDateRange returns range and an onPressDate handler, and you pass selectedRange (or selectedDates) plus minDate / maxDate / isDateDisabled to MonthList. Selection is driven by clicking days; the react-dom renderer does not implement pointer drag-select.

Keyboard navigation

Tab moves through events only. Empty day columns (time grid) and empty day cells (month) are not tab stops, so keyboard focus lands on each event in turn rather than stopping on every blank cell. Events are buttons: Enter or Space activates them (onPressEvent). This is a deliberate default. The alternative, making every empty cell focusable, turns a week into seven tab stops before the first event and a month into dozens. Creating on empty space stays pointer-only (drag to sweep out a range); if you want a keyboard way to add an event, render your own “New event” button. Three cases add richer keyboard navigation:
  • The date picker (MonthList or MonthView rendered without events) is always keyboard-navigable: one roving tab stop per month, arrow keys move the focus, Enter selects the day. Date selection is the whole point there.
  • Month with events, opt in with keyboardDayNavigation to add the same roving day focus on top of the event chips, so Enter on a focused day opens it (onPressDay). It is off by default.
  • Time grid with events, opt in with keyboardEventNavigation to add arrow-key movement between events: Up/Down step through a day by time, Left/Right jump to the nearest event in the adjacent day, Home/End go to the day’s first/last event, and Enter/Space activate. This is additive: every event stays individually tabbable, so screen-reader users keep full access and the arrow keys are a convenience for sighted keyboard users. Off by default. The day-column headers always carry a full-date label for screen readers, whether or not they’re interactive.
keyboardDayNavigation and keyboardEventNavigation are web-only concerns, so they exist on the react-dom renderer only. The React Native renderer pages with the arrow keys on react-native-web instead.

Paging with the keyboard

Pass onChangeDate and the focused calendar pages with PageDown / PageUp: a month in month, a week in schedule, otherwise the view’s day span. It’s controlled, so update date in response (the same handler you’d wire to your own prev/next buttons). Omit onChangeDate and the keys fall through untouched.

Theming

The DOM theme is a flat object of colors plus a few metrics, separate from the React Native CalendarTheme (which carries TextStyle/ViewStyle). Both derive their colors from the same core tokens, so the palettes match and the color-token list applies here too (just flat, not nested under colors).
The theme recolours everything at once. To hand a specific part of a view to Tailwind or your own CSS — with data-* state variants like data-[today]: — use the per-slot classNames / styles props. See Styling with Tailwind & CSS. See it running on the live demo and in the examples/web Vite app.