Docyrus

Calendar

Full calendar for React Native — month, week, day, agenda and year views plus year-timeline, quarter and 13-week period timelines, with drag & drop, resize, user / color filters, settings and event sheets.

iOSAndroid
Preview Calendar on your device

Scan with Expo Go

Download Expo Go, then scan the QR code to preview native components.

Installation

pnpm dlx @docyrus/cli add @docyrus/rn-calendar
Required Packages(4 packages)
pnpm add date-fns react-native-gesture-handler react-native-reanimated @shopify/flash-list

The native calendar mirrors the web Calendar API 1:1 (same IEvent model, same prop names, same ui.calendar.* translation keys) and adapts it to touch: popovers become bottom sheets, right-click / hover become long-press, HTML5 drag becomes a gesture-handler long-press drag with a reanimated ghost.

Expo Configuration

Month and weekday names use the device locale through the optional react-native-localize peer. Add its plugin to app.config.ts when you install it:

{
  plugins: [
    ['react-native-localize', { locales: ['en', 'tr'] }]
  ]
}

The app must be wrapped in GestureHandlerRootView (drag & drop, resize, taps).

Usage

import { Calendar, type IEvent } from '@/components/docyrus-native/calendar';

const events: IEvent[] = [
  {
    id: 1,
    title: 'Team Meeting',
    startDate: '2026-03-04T10:00:00.000Z',
    endDate: '2026-03-04T11:00:00.000Z',
    color: 'blue',
    description: 'Weekly sync',
    user: { id: 'u1', name: 'Ada Lovelace', picturePath: null }
  }
];

<Calendar
  events={events}
  users={[{ id: 'u1', name: 'Ada Lovelace', picturePath: null }]}
  defaultView="month"
  weekStartsOn={1}
  onEventCreate={event => api.create(event)}
  onEventUpdate={event => api.update(event)}
  onEventDelete={id => api.remove(id)}
  onEventDrop={(event, start, end) => api.move(event.id, start, end)}
  onVisibleRangeChange={(range, view) => refetch(range)}
/>

Period timelines

<Calendar
  events={projectPlans}
  defaultView="year-timeline"
  visibleViews={['year-timeline', 'quarter', '13weeks']}
  defaultYearTimelineMode="continuous"
  defaultQuarterMode="single"
  timelineMaxLanes={4}
/>

Refetch without resetting the view

const { data, isFetching, isLoading } = useQuery(/* … */);

<Calendar
  events={data ?? []}
  isLoading={isLoading}      // first load → skeleton (unmounts)
  isRefreshing={isFetching}  // later fetches → overlay, keeps view / date / scroll
/>

Controlled / persisted settings

// Persist the settings blob (badge style, 24h format, agenda grouping,
// timeline modes, last view) through lib/storage — mount a sync store with
// <DocyStorageProvider> (expo-sqlite kv-store or MMKV).
<Calendar events={events} persistSettings />

// Or own the settings yourself:
<Calendar
  events={events}
  settings={{ use24HourFormat: false }}
  onSettingsChange={settings => save(settings)}
/>

Custom create / open flows

<Calendar
  events={events}
  onCreateEvent={date => router.push({ pathname: '/events/new', params: { date: date.toISOString() } })}
  onEventClick={event => router.push(`/events/${event.id}`)}
/>

Views

ViewDescription
monthMonth grid; multi-day bars keep their row across cells, +N opens a sheet with the day's events. Tap an empty cell to create.
week7-day time grid (startHour–endHour), all-day / multi-day row, now-line, overlap packing. Tap a slot to create (15-minute precision).
daySingle-day time grid with the same features as week.
agendaThe month's events, searchable, grouped by date or by color (settings).
yearTwelve mini months with event dots; tap a month → month view, tap a day → day view.
year-timelineYear → 4 quarter rows → month groups → week columns; bars span the weeks they cover.
quarterQuarter → 3 month rows → week groups → day columns.
13weeksA floating 13-week window (anchored at the selected week, ±91 days per step) with a weekday header and per-row date strip.

Timeline views scroll horizontally on phones with a sticky row gutter. year-timeline and quarter support continuous (periods stacked, more load on scroll, capped at ±12) and single navigation; 13weeks is always single. A row shows timelineMaxLanes lanes and collapses the rest into +N more (only that row expands).

Interactions

GestureResult
Tap an eventonEventClick / onEventPress, otherwise the built-in details sheet (title, responsible user, start / end, description, Edit / Delete with confirmation).
Long-press an event, then dragMoves it. Month cells keep the time of day, week / day columns snap to 15 minutes, timeline week cells keep the weekday. Fires onEventUpdate then onEventDrop.
Drag the bottom handle of a week / day blockResizes it (15-minute snap, minimum 15 minutes, never past the start day). Fires onEventUpdate.
Tap an empty cell / slot / header "Add Event" / FABonCreateEvent(date) → else onSlotClick(start, end) → else the built-in form.
Tap a month cell with events or +NSheet listing the day's events.

API Reference

Calendar

PropTypeDefaultDescription
eventsIEvent[][]Events. A new array replaces the calendar's local copy.
usersIUser[][]Users for the header filter select.
defaultViewTCalendarView'month'Initial view. A later change switches the view (host-driven view switcher).
defaultDateDatetodayInitially selected date (clamped to minDate / maxDate).
visibleViewsTCalendarView[]all eightViews shown in the tab strip.
weekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 60First day of the week (date-fns, 0 = Sunday). Drives month grid, week view, week numbers and the 13-week window.
minDateDate—Navigation never moves before this date (prev arrow disables).
maxDateDate—Navigation never moves past this date (next arrow disables).
isLoadingbooleanfalseRender a skeleton instead of the calendar (unmounts it).
isRefreshingbooleanfalseSpinner overlay over the body that keeps view, date and scroll position.
variant'default' | 'bordered' | 'compact''default'Visual style.
size'sm' | 'default' | 'lg''default'Header text density.
badge'dot' | 'colored''colored'Initial badge style (seeds the settings).
showFabbooleanfalseFloating "+" button that opens the create flow.
onCreateEvent(date: Date) => void—Replaces the built-in create form.
onEventCreate(event: IEvent) => void—Called after an event is added (built-in form).
onEventUpdate(event: IEvent) => void—Called after an event is updated (form, resize, drag).
onEventDelete(eventId: TEventId) => void—Called after an event is deleted.
onEventClick(event: IEvent) => void—Replaces the built-in details sheet.
onEventPress(event: IEvent) => void—Native alias of onEventClick (onEventClick wins).
onDaySelect(date: Date) => void—Any day cell / day header tapped, independent of creation.
onSlotClick(start: Date, end: Date) => void—Empty cell / slot tapped when onCreateEvent is not set (bypasses the form).
onVisibleRangeChange(range: { start: Date; end: Date }, view: TCalendarView) => void—The visible window changed. Timeline views report the window they rendered (debounced 150 ms, reset when the view changes).
onEventDrop(event: IEvent, newStart: Date, newEnd: Date) => void—Called after drag & drop moves an event (receives the original event).
onDateChange(date: Date) => void—Selected date changed.
onViewChange(view: TCalendarView) => void—Active view changed.
hideAddButtonbooleanfalseDeprecated — hides both header and cell add.
hideHeaderAddButtonbooleanfalseHide the header "Add Event" button.
hideCellAddButtonbooleanfalseTapping an empty cell / slot no longer opens the create flow.
hideUserSelectbooleanfalseHide the user / filter select.
hideSettingsbooleanfalseHide the settings button.
hideFilterbooleanfalseHide the color filter strip.
hideColorFilterboolean—Native alias of hideFilter.
hideViewTabsbooleanfalseHide the view tab strip.
hideNavigationbooleanfalseHide the today / title / arrows row.
hideEventCountbooleanfalseHide the event count badge.
addButtonLabelstringt('ui.calendar.addEvent', 'Add Event')Header add button label.
eventCountLabelstringt('ui.calendar.events', 'events')Event count badge label (e.g. "entries"). Timeline views count events overlapping the rendered window.
searchPlaceholderstringt('ui.calendar.searchPlaceholder', 'Search events...')Agenda search placeholder.
filterItemsCalendarFilterItem[]—Generic filter items; replaces the users list.
onFilterChange(id: string) => void—Filter item picked ('all' resets).
renderFilterSelect(context: CalendarContextValue) => ReactNode—Fully replaces the header filter select.
sidebarContentReactNode—Content of a right-side drawer opened from a header button.
renderEventBadge(event: IEvent, defaultBadge: ReactNode) => ReactNode—Custom month-view badge renderer.
cellMinHeightnumber52Minimum height (px) of a month cell.
cellMinWidthnumber—Minimum width (px) of a month cell.
hourSlotHeightnumber60Height (px) of one hour in week / day view.
startHournumber0First hour of the week / day grid (0-23).
endHournumber24End hour of the week / day grid (1-24, exclusive).
timelineMaxLanesnumber3Timeline lanes a row shows before +N more.
defaultYearTimelineMode'continuous' | 'single''continuous'Initial year-timeline pagination (a later change is applied).
defaultQuarterMode'continuous' | 'single''continuous'Initial quarter pagination (a later change is applied).
settingsPartial<CalendarSettings>—Controlled settings — every supplied key wins over the internal state.
onSettingsChange(settings: CalendarSettings) => void—Called with the full settings blob on every change.
persistSettingsboolean | { storage?: 'session' | 'local'; key?: string }falsePersist the settings through lib/storage (true → 'local', key 'calendar-settings').
classNamestring—Root container classes.
styleViewStyle—Root container style.

CalendarProvider accepts every prop above except isLoading, variant, size, showFab, className and style, plus children.

IEvent

FieldTypeDescription
idTEventId (number | string)Unique id.
startDatestringISO start.
endDatestringISO end. An event from 00:00 to 23:59 (or spanning days) renders in the all-day row.
titlestringTitle.
colorTEventColor'blue' | 'green' | 'red' | 'yellow' | 'purple' | 'orange' — resolved through the theme palette.
descriptionstringDescription (may be empty).
userIUserResponsible user.

IUser

FieldTypeDescription
idstringUser id.
namestringDisplay name.
picturePathstring | nullAvatar URL.

CalendarFilterItem

FieldTypeDescription
idstringItem id passed to onFilterChange.
labelstringLabel.
imagestring | nullOptional avatar URL.

CalendarSettings

FieldTypeDefaultDescription
badgeVariant'dot' | 'colored''colored'Badge style.
viewTCalendarViewdefaultViewActive view.
use24HourFormatbooleantrue24-hour times.
agendaModeGroupBy'date' | 'color''date'Agenda grouping.
yearTimelineMode'continuous' | 'single''continuous'Year-timeline pagination.
quarterMode'continuous' | 'single''continuous'Quarter pagination.

The header settings sheet edits every field except view (the tab strip does).

useCalendar()

Returns CalendarContextValue: selectedDate / setSelectedDate, view / setView, the settings getters + setters (badgeVariant, use24HourFormat / toggleTimeFormat, agendaModeGroupBy, yearTimelineMode, quarterMode, settings), filters (selectedColors, filterEventsBySelectedColors, selectedUserId, filterEventsBySelectedUser, selectedFilterId, onFilterChange, clearFilter), events (filtered) / allEvents, CRUD (addEvent, updateEvent, removeEvent, moveEvent), flows (requestCreate, openEvent, openEditForm, openDayEvents), the grid config (weekStartsOn, startHour, endHour, hourSlotHeight, cellMinHeight, cellMinWidth, timelineMaxLanes, minDate, maxDate) and timelineRange / reportTimelineRange.

Components

ComponentDescription
CalendarThe full calendar.
CalendarViewAlias of Calendar (previous native name).
CalendarProviderContext provider (for custom layouts built with useCalendar).
CalendarTimelineViewThe period-timeline engine used by year-timeline / quarter / 13weeks, usable standalone.

Type Exports

TypeDescription
CalendarPropsProps of Calendar.
CalendarProviderPropsProps of CalendarProvider.
CalendarContextValue / ICalendarContextValue returned by useCalendar().
CalendarSettingsSettings blob.
CalendarPersistSettingspersistSettings option.
CalendarVisibleRange{ start: Date; end: Date }.
CalendarBadgeVariant'dot' | 'colored'.
CalendarAgendaGroupBy'date' | 'color'.
IEvent, IUser, ICalendarCell, CalendarFilterItem, TEventIdEvent model.
TCalendarView'day' | 'week' | 'month' | 'year' | 'agenda' | 'year-timeline' | 'quarter' | '13weeks'.
TTimelineView'year-timeline' | 'quarter' | '13weeks'.
TTimelineNavigationMode'continuous' | 'single'.
TEventColor'blue' | 'green' | 'red' | 'yellow' | 'purple' | 'orange'.
TWeekStartsOn0 | 1 | 2 | 3 | 4 | 5 | 6.
CalendarTimelineViewProps, TimelineColumn, TimelineColumnGroup, TimelineRow, TimelineSection, TimelineVariantTimeline engine types.

Translations

All copy goes through useUiTranslation() with the web ui.calendar.* keys (addEvent, settings, dotBadge, 24HourFormat, agendaGroupBy, yearTimelineDisplay, quarterDisplay, continuous, singleYear, singleQuarter, viewMonth … view13Weeks, previousMonth / nextQuarter … nextPeriod, eventsOn, noEvents, noResults, searchPlaceholder, responsible, startDate, endDate, deleteEvent, deleteConfirm, loading, more, showLess, …). Native-only keys: ui.calendar.today, ui.calendar.events, ui.calendar.allDay, ui.calendar.startTime, ui.calendar.selectDate, ui.calendar.selectTime, ui.calendar.endTimeError, ui.calendar.currentUser, ui.calendar.event, ui.calendar.sidebar, ui.calendar.resizeEvent.

Migration from the previous native API

BeforeNow
CalendarEvent { id: string; start: Date; end: Date; color?: hex; allDay? }IEvent { id: number | string; startDate: ISO; endDate: ISO; color: TEventColor; description; user } (all-day = 00:00–23:59 or multi-day)
CalendarViewMode (5 views)TCalendarView (8 views)
CalendarViewPropsCalendarProps
firstDayOfWeek: 0 | 1weekStartsOn: 0–6
views (deprecated)removed — use visibleViews
onEventCreate(event without id)onEventCreate(event: IEvent) (id generated)
onEventDelete(id: string)onEventDelete(id: TEventId)
showFab default truedefault false (header "Add Event" button is shown instead)
visibleViews default month/week/day/agendaall eight views
action-sheet event menudetails bottom sheet + delete confirm

On this page