Migrate to expo-calendar/next

Edit page

Migrate from the legacy expo-calendar API to the new class-based expo-calendar/next API with ExpoCalendar, ExpoCalendarEvent, and hooks.


For the complete documentation index, see llms.txt. Use this file to discover all available pages.

The expo-calendar/next API is now stable. The legacy expo-calendar module is deprecated. Migrate to expo-calendar/next to benefit from the new API and future fixes.

The new API replaces free functions that accepted IDs with methods on class instances. Calendars, events, reminders, and attendees are now represented as class instances with their own methods. Key changes:

  • Operations on calendars, events, reminders, and attendees are now methods on the corresponding instance instead of free functions that accept an ID.
  • createCalendar, createEvent, and createReminder return class instances instead of string IDs.

Installation

Install the SDK-compatible package that includes expo-calendar/next:

Terminal
npx expo install expo-calendar

Importing the new API

Import from expo-calendar/next:

import { ExpoCalendar, ExpoCalendarEvent } from 'expo-calendar/next';

Calendars

Create a calendar

// Before const calendarId = await Calendar.createCalendarAsync({ title: 'My Calendar', color: '#ff0000' }); // After const calendar = await createCalendar({ title: 'My Calendar', color: '#ff0000' });

createCalendar returns an ExpoCalendar instance, not just an ID.

List calendars

// Before const calendars = await Calendar.getCalendarsAsync(Calendar.EntityTypes.EVENT); // After const calendars = await getCalendars(EntityTypes.EVENT);

Get a calendar by ID

// Before // No direct equivalent, had to filter from getCalendarsAsync // After const calendar = await ExpoCalendar.get(calendarId);

Update a calendar

// Before await Calendar.updateCalendarAsync(calendarId, { title: 'Renamed' }); // After await calendar.update({ title: 'Renamed' });

Delete a calendar

// Before await Calendar.deleteCalendarAsync(calendarId); // After await calendar.delete();

Get default calendar (iOS only)

// Before const calendar = await Calendar.getDefaultCalendarAsync(); // After const calendar = getDefaultCalendarSync();

Show a calendar picker (iOS only)

// Before // No equivalent // After const calendar = await presentPicker(); if (calendar) { // user selected a calendar }

presentPicker returns null if the app user dismisses the picker without selecting a calendar.

Events

Create an event

// Before const eventId = await Calendar.createEventAsync(calendarId, { title: 'Lunch', startDate, endDate, }); // After const event = await calendar.createEvent({ title: 'Lunch', startDate, endDate });

createEvent returns an ExpoCalendarEvent instance, not just an ID.

List events in a calendar

// Before const events = await Calendar.getEventsAsync([calendarId], startDate, endDate); // After const events = await calendar.listEvents(startDate, endDate);

List events across multiple calendars

// Before const events = await Calendar.getEventsAsync([id1, id2], startDate, endDate); // After const events = await listEvents([calendar1, calendar2], startDate, endDate);

Get an event by ID

// Before const event = await Calendar.getEventAsync(eventId); // After const event = await ExpoCalendarEvent.get(eventId);

Update an event

// Before await Calendar.updateEventAsync(eventId, { title: 'Lunch with Alex' }); // After await event.update({ title: 'Lunch with Alex' });

The legacy updateEventAsync accepted recurringEventOptions (iOS only) to target a single occurrence or future occurrences of a recurring event. This is not supported in the new API — update() always modifies the entire recurring event series.

Delete an event

// Before await Calendar.deleteEventAsync(eventId); // After await event.delete();

The legacy deleteEventAsync accepted recurringEventOptions (iOS only) to target a single occurrence or future occurrences of a recurring event. This is not supported in the new API — delete() always deletes the entire recurring event series.

Open event in calendar

// Before await Calendar.openEventInCalendarAsync(params); // After await event.openInCalendar(params);

The id field is no longer part of params — it comes from the event instance. Presentation options (allowsEditing, allowsCalendarPreview, startNewActivityTask) are now passed in the same params object instead of as a separate argument.

Edit event with native form

// Before await Calendar.editEventInCalendarAsync(params); // or, to create a new event with form await Calendar.createEventInCalendarAsync({ title, startDate, endDate }); // After await event.editInCalendar(params); // or, to create a new event with form await calendar.addEventWithForm({ title, startDate, endDate });

The id field is no longer part of params — it comes from the event instance. Presentation options (startNewActivityTask) are now passed in the same params object instead of as a separate argument.

Get recurring event occurrence

// Before const event = await Calendar.getEventAsync(eventId, { instanceStartDate }); // After const event = await ExpoCalendarEvent.get(eventId); const occurrence = event.getOccurrenceSync({ instanceStartDate });

Attendees

Get attendees of an event

// Before const attendees = await Calendar.getAttendeesForEventAsync(eventId); // After const attendees = await event.getAttendees();

Add an attendee

// Before const attendeeId = await Calendar.createAttendeeAsync(eventId, { email: 'alex@example.com', name: 'Alex', role: Calendar.AttendeeRole.ATTENDEE, type: Calendar.AttendeeType.PERSON, status: Calendar.AttendeeStatus.ACCEPTED, }); // After const attendee = await event.createAttendee({ email: 'alex@example.com', name: 'Alex' });

Update an attendee (Android only)

// Before await Calendar.updateAttendeeAsync(attendeeId, { name: 'Alexander' }); // After await attendee.update({ name: 'Alexander' });

Delete an attendee (Android only)

// Before await Calendar.deleteAttendeeAsync(attendeeId); // After await attendee.delete();

Reminders (iOS only)

Create a reminder

// Before const reminderId = await Calendar.createReminderAsync(calendarId, { title: 'Buy milk' }); // After const reminder = await calendar.createReminder({ title: 'Buy milk' });

createReminder returns an ExpoCalendarReminder instance, not just an ID.

List reminders

// Before const reminders = await Calendar.getRemindersAsync([calendarId], status, startDate, endDate); // After const reminders = await calendar.listReminders(startDate, endDate, status);

Get a reminder by ID

// Before const reminder = await Calendar.getReminderAsync(reminderId); // After const reminder = await ExpoCalendarReminder.get(reminderId);

Update a reminder

// Before await Calendar.updateReminderAsync(reminderId, { title: 'Buy oat milk' }); // After await reminder.update({ title: 'Buy oat milk' });

Delete a reminder

// Before await Calendar.deleteReminderAsync(reminderId); // After await reminder.delete();

Sources

// Before const sources = await Calendar.getSourcesAsync(); // After const sources = getSourcesSync();

getSourcesAsync is replaced by the synchronous getSourcesSync. Fetching a single source by ID has no direct equivalent in the new API.

Permissions

// Before await Calendar.requestCalendarPermissionsAsync(); await Calendar.getCalendarPermissionsAsync(); await Calendar.requestRemindersPermissionsAsync(); await Calendar.getRemindersPermissionsAsync(); // After await requestCalendarPermissions(); await getCalendarPermissions(); await requestRemindersPermissions(); await getRemindersPermissions();

useCalendarPermissions and useRemindersPermissions hooks are unchanged.

Breaking semantic changes

  • Calendars, events, reminders, and attendees are now class instances. Operations are methods on the instance instead of free functions that accept an ID. Use the corresponding .get(id) static method to obtain an instance if you only have an ID.
  • createCalendar, createEvent, and createReminder return class instances instead of string IDs.
  • The Async suffix is dropped. The majority of the library is asynchronous — only synchronous functions use a Sync suffix (for example, getDefaultCalendarSync, getSourcesSync, getOccurrenceSync).
  • getSourcesAsync is replaced by the synchronous getSourcesSync. Fetching a single source by ID has no direct equivalent.
  • createEventInCalendarAsync is renamed to calendar.addEventWithForm.
  • openEventInCalendar (the fire-and-forget sync variant) is removed. Use event.openInCalendar() instead.
  • Attendee operations are now instance methods: creating an attendee is done via event.createAttendee() on an ExpoCalendarEvent instance; updating and deleting are methods on the resulting ExpoCalendarAttendee instance.

Reference

Calendar (next)

See the full API reference for expo-calendar/next.