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, andcreateReminderreturn class instances instead of string IDs.
Installation
Install the SDK-compatible package that includes expo-calendar/next:
- npx expo install expo-calendarImporting 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, andcreateReminderreturn class instances instead of string IDs.- The
Asyncsuffix is dropped. The majority of the library is asynchronous — only synchronous functions use aSyncsuffix (for example,getDefaultCalendarSync,getSourcesSync,getOccurrenceSync). getSourcesAsyncis replaced by the synchronousgetSourcesSync. Fetching a single source by ID has no direct equivalent.createEventInCalendarAsyncis renamed tocalendar.addEventWithForm.openEventInCalendar(the fire-and-forget sync variant) is removed. Useevent.openInCalendar()instead.- Attendee operations are now instance methods: creating an attendee is done via
event.createAttendee()on anExpoCalendarEventinstance; updating and deleting are methods on the resultingExpoCalendarAttendeeinstance.
Reference
See the full API reference for expo-calendar/next.