A generic, themeable month / week / day calendar for React Native.

Documentation · Quickstart · Live demo · API reference
@legendapp/listCalendarEvent<T>) and a renderEvent escape hatchuseDateRange), disabled days, and a scrolling MonthListuseMonthGrid hook to build a fully custom calendarThis is a ground-up reimagining inspired by the excellent
react-native-big-calendar.
It keeps the familiar month/week/day model but is built around Reanimated and
modern list virtualization — trading framework-agnosticism for a richer,
gesture-driven experience. It's not a fork; the API differs, and the name is
an homage. 🙇
Already using react-native-big-calendar? The migration guide has a copy-paste prompt for your coding agent plus a manual prop mapping.
| Capability | super-calendar | react-native-big-calendar |
|---|---|---|
| Month / week / day / 3-day / custom / agenda | ✅ | ✅ |
Generic event typing (CalendarEvent<T>) |
✅ | ✅ |
| Virtualized, snap-paged views | ✅ | ❌ renders all dates |
| Pinch-to-zoom (native) / Ctrl-scroll (web) | ✅ | ❌ |
| Drag to move & resize events | ✅ | ❌ (declined upstream) |
| Date selection (single / range / multiple) | ✅ useDateRange + disabled days |
❌ |
Headless grid hook (useMonthGrid) |
✅ | ❌ |
| Overlapping events | ✅ side-by-side columns | ⚠️ stacked / indented |
Month paging fires onChangeDate |
✅ | ⚠️ known gaps |
| Recurring events | ✅ expandRecurringEvents |
❌ expand them yourself |
| Time-zone display | ✅ eventsInTimeZone |
❌ |
| Dark mode | ✅ darkTheme preset |
❌ bring your own palette |
renderEvent across every mode & event type |
✅ | ⚠️ breaks for all-day/multi-day |
| Web | ✅ arrow-key paging, Ctrl-scroll zoom | ⚠️ partial |
| Runtime dependencies | Reanimated + Gesture Handler + Legend List | dayjs + calendarize (lighter) |
Legend: ✅ supported · ⚠️ partial or with known issues · ❌ not available. The last row is the honest trade-off: big-calendar has a smaller footprint and fewer native peers, so it can be the simpler choice when you don't need the gestures, virtualization, or the helpers above.
What it adds over react-native-big-calendar
@legendapp/list) — swipe across
years of dates, with native one-page paging (or opt into freeSwipe).CalendarEvent<T> carries your
own fields, and renderEvent is a component (so it may use hooks) that receives
the event box's live pixel height for progressive disclosure as the grid zooms.Feature parity. It also covers the rest of react-native-big-calendar's
surface: month / day / 3-day / week / custom N-day (and weekEndsOn
partial-weeks) / agenda (schedule) modes, all-day events (lane +
allDay flag, toggle the lane with showAllDayEventCell), multi-day clipping,
minHour/maxHour, ampm (hour axis and event times), showTime, timeslots,
hideHours, showWeekNumber, weekNumberPrefix, hourComponent,
sortedMonthView, moreLabel, showAdjacentMonths, showSixWeeks,
disableMonthEventCellPress, a default month weekday header
(renderHeaderForMonthView), a custom month date badge
(renderCustomDateForMonth), activeDate, per-event
disabled, onPress/onLongPress for events, cells and date headers,
onChangeDateRange, resetPageOnPressCell, swipeEnabled,
verticalScrollEnabled, showVerticalScrollIndicator, an agenda
itemSeparatorComponent, eventCellStyle, calendarCellStyle, a
headerComponent slot, date-fns locale, right-to-left column order (isRTL),
and theming. Text styling that big-calendar exposes via calendarCellTextStyle
is covered by CalendarTheme.text; overlapping events are laid out in
side-by-side columns automatically.
Trade-offs (where react-native-big-calendar may suit you better)
@legendapp/list are required. react-native-big-calendar is more
self-contained (no Reanimated/Gesture Handler).isRTL reverses the day-column order, like
big-calendar's): the hour gutter stays on the left and paging follows the
system scroll direction. Enable React Native's I18nManager for full RTL.The date-picker surface (MonthList, useDateRange, and the headless
useMonthGrid) is inspired by
flash-calendar, an excellent
headless date picker for React Native. If you only need date selection,
flash-calendar is the lighter, more focused choice: a dedicated, FlashList-based
picker with no event model. super-calendar folds picking into a
full gesture calendar, so one library covers events and date selection, at the
cost of the Reanimated, Gesture Handler, and Legend List peers. Pick
flash-calendar for a standalone picker; pick this when you also need the event
views.
npm install @super-calendar/native
Also published on JSR: deno add jsr:@super-calendar/native (or npx jsr add @super-calendar/native).
The full calendar relies on the following being installed in your app:
npm install react-native-reanimated react-native-worklets react-native-gesture-handler @legendapp/list date-fns
Make sure Reanimated and Gesture Handler are set up per their own docs (Babel
plugin, GestureHandlerRootView at the root of your app).
These are declared as optional peers so web-only installs (@super-calendar/dom)
and the picker (the @super-calendar/native/picker subpath) aren't asked to
install React Native packages they don't use. The full calendar still needs them:
because its components import Reanimated
and Gesture Handler directly, a missing one surfaces as a clear Metro
Unable to resolve "react-native-reanimated" build error rather than a silent
failure, so install the line above when you use Calendar or the time grid.
If you only need date selection, import it from the
@super-calendar/native/picker subpath. It contains the month grid,
selection, and the headless useMonthGrid, with none of the timetable code and
no Reanimated dependency, so it works on every bundler (Metro included) without
shipping the week/day grid. A picker-only app installs just:
npm install react-native-gesture-handler @legendapp/list date-fns
import { MonthList, useDateRange } from "@super-calendar/native/picker";
react-native-reanimated and react-native-worklets are declared as optional
peers, so this subpath won't pull them in. (Metro doesn't tree-shake the
@super-calendar/native barrel, so the subpath is what guarantees the timetable
code is left out.)
For a plain react-dom app (no React Native, no react-native-web), install the
@super-calendar/dom package. It ships real DOM components,
MonthView, MonthList (the date picker), and TimeGrid (day/week/N-day, with
Ctrl/⌘-scroll and pinch zoom plus drag to move and resize), built on the same
pure core and Legend List's DOM renderer. A web app installs just:
npm install @super-calendar/dom react react-dom @legendapp/list date-fns
import { MonthList, TimeGrid, useDateRange } from "@super-calendar/dom";
The React Native peers (react-native, react-native-gesture-handler,
react-native-reanimated, react-native-worklets) are all optional, so a web
install pulls none of them. Styling is plain inline styles driven by a theme
prop (defaultDomTheme / darkDomTheme), no stylesheet import required.
A selected range renders as a centered rounded "pill" band by default (its
height and colour are the rangeBandHeight / rangeBackground theme tokens).
Pass fillCellOnSelection to MonthView / MonthList to fill the whole cell
edge to edge instead.
Want the date math and selection model without any of the built-in UI? The
@super-calendar/core package exports just the pure pieces,
buildMonthGrid / useMonthGrid, useDateRange and the selection helpers,
layoutDayEvents, and the date utilities, with zero React Native, Reanimated, or
Legend List imports. It's what the DOM components are built on, and it works in
any React renderer (react-dom, Solid via its React compat, your own).
npm install @super-calendar/core react date-fns
import { buildMonthGrid, nextDateRange } from "@super-calendar/core";
import { useState } from "react";
import { Calendar, type CalendarEvent } from "@super-calendar/native";
type MyEvent = { id: string; color: string };
const events: CalendarEvent<MyEvent>[] = [
{
id: "1",
color: "#1F6FEB",
title: "Lecture",
start: new Date(2026, 5, 19, 10, 0),
end: new Date(2026, 5, 19, 11, 30),
},
];
export function MyCalendar() {
const [mode, setMode] = useState<"month" | "week" | "day">("week");
const [date, setDate] = useState(new Date());
return (
<Calendar
mode={mode}
date={date}
events={events}
weekStartsOn={1}
onChangeDate={setDate}
onPressEvent={(event) => console.log(event.id)}
onPressDay={(day) => {
setDate(day);
setMode("day");
}}
/>
);
}
The built-in renderer draws a simple titled box. Pass renderEvent — a React
component, not a callback — to take full control. Because it's rendered as a
component, it may use hooks. The same renderer is used in every mode — month
chips, the all-day lane, the timed grid and the schedule list — and always
receives isAllDay (plus continuesBefore/continuesAfter for clipped
multi-day segments on the grid), so one component covers them all. On the
week/day grid you also receive boxHeight, a Reanimated shared value tracking
the live pixel height of the box (driven by pinch-zoom), so you can reveal
detail progressively without re-rendering:
```tsx import Animated, { useAnimatedStyle } from "react-native-reanimated"; import { Pressable, Text } from "react-native"; import type { RenderEventArgs } from "@super-calendar/native";
// Define the component once (don't inline it, or it remounts every render). function MyEvent({ event, boxHeight, onPress }: RenderEventArgs) { const detailStyle = useAnimatedStyle(() => ({ display: (boxHeight?.value ?? Infinity) >= 84 ? "flex" : "none", })); return ( <Pressable style={{ flex: 1, backgroundColor: e
$ claude mcp add super-calendar \
-- python -m otcore.mcp_server <graph>