@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 amode and pass events.
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 (
MonthListorMonthViewrendered withoutevents) 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
keyboardDayNavigationto 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
keyboardEventNavigationto 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
PassonChangeDate 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 NativeCalendarTheme (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).
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.