> For the complete documentation index, see [llms.txt](https://docs.buildnatively.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.buildnatively.com/natively-platform/features/calendars.md).

# Calendars

## What is Calendars?

The Calendars feature gives your app access to the device's native calendars, letting users retrieve their existing calendars or create new events without leaving the app. This is useful for features like adding a booking confirmation, an appointment, or a reminder directly to the user's calendar.

Accessing calendars requires the user to grant permission the first time the feature is used, on both iOS and Android.

## Prerequisites

{% hint style="success" %}
This feature requires any **paid** plan. [See all plans](/getting-started/subscription-plans.md)
{% endhint %}

* [Contacts](/natively-platform/features/contacts.md) feature is already enabled in the Natively Dashboard.

## Natively Dashboard Setup

{% hint style="warning" %}
Make sure [Contacts](/natively-platform/features/contacts.md) is already enabled; Calendars can't be enabled otherwise.
{% endhint %}

1. Open your Natively app dashboard and navigate to **Features** > **Calendars**.
2. Toggle the feature to **Enabled**.
3. Enter the **Permission Description**.
4. Click **Save**.
5. Rebuild your app(s).

{% hint style="info" %}
The **Permission Description** is the text shown to users when the OS asks them to grant **calendar** access. Explain clearly why your app needs this permission - for example: "We use your calendar to let you save appointments directly to your device".
{% endhint %}

{% hint style="warning" %}
You must rebuild your app for these changes to take effect.
{% endhint %}

## Implementation

Choose your integration method below: **Bubble.io Plugin** (No-Code), **JavaScript SDK** (Code), or **AI Agents** (for AI-powered editors like Lovable, Base44, and Replit).

### Initialization

{% tabs %}
{% tab title="Bubble.io Plugin" %}
**Check Plugin**

Before starting, verify if the Natively plugin is already installed in your Bubble project.

1. Open your Bubble editor and navigate to the Plugins tab in the left sidebar.
2. **Check Installed Plugins:** Look through your list of installed plugins for "Natively iOS & Android app builder".
   * If it IS installed: Check the version number. If an update is available (e.g., you see a button saying "Update"), click it to ensure you have the latest features and bug fixes.

<figure><img src="https://docs.buildnatively.com/~gitbook/image?url=https%3A%2F%2F3352617162-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F90tV7pYflEQdiAr2VfWu%252Fuploads%252FmfnSUug82IdnxOAoBrak%252Fnatively_app_builder_bubble_plugin_update.png%3Falt%3Dmedia%26token%3Dc193f69f-b03b-4be4-b80b-f34ba37ac212&#x26;width=768&#x26;dpr=3&#x26;quality=100&#x26;sign=a89e4510&#x26;sv=2" alt=""><figcaption></figcaption></figure>

* If it is NOT installed: Click the + Add plugins button, search for "Natively", and click Install.

<figure><img src="https://docs.buildnatively.com/~gitbook/image?url=https%3A%2F%2F3352617162-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252F90tV7pYflEQdiAr2VfWu%252Fuploads%252FC5rA42yQHcN1uGKFbzmF%252Fnatively_app_builder_bubble_plugin.png%3Falt%3Dmedia%26token%3Dd9706d9b-dbe8-459b-b9b3-5667648aa4b7&#x26;width=768&#x26;dpr=3&#x26;quality=100&#x26;sign=9aae2297&#x26;sv=2" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="JavaScript SDK" %}
**Check SDK**

Before writing any logic, ensure the Natively SDK is correctly installed and up-to-date in your codebase.

1. Open your project's main HTML file (or header settings) and look for the Natively script tag inside the `<head>` section.
2. Install/Update: If missing or outdated, add the following code. You can specify the SDK version in the URL (e.g., `@2.26.0`).

```javascript
<head>
  <script async onload="nativelyOnLoad()" src="https://cdn.jsdelivr.net/npm/natively@2.26.0/natively-frontend.min.js"></script>
</head>
```

{% hint style="info" %}
To ensure you are using the most up-to-date version, check the [Natively GitHub releases page](https://github.com/buildnatively/js-sdk/releases) for the latest version number.
{% endhint %}
{% endtab %}

{% tab title="AI Agents" %}
**Initialize the SDK**

AI-powered editors like Lovable, Base44, and Replit use the JavaScript SDK to implement Natively features. Before implementing any feature, the Natively SDK must be initialized in your project.

Copy the line below and paste it into your AI agent to check and set up the Natively SDK in your project.

```
Check if the following Natively SDK script is present in the <head> of index.html. If missing or outdated, add it: <script async onload="nativelyOnLoad()" src="https://cdn.jsdelivr.net/npm/natively@2.26.0/natively-frontend.min.js"></script><script>function nativelyOnLoad() { window.natively.setDebug(true); console.log("✅ Natively SDK loaded successfully."); }</script> For reference: https://docs.buildnatively.com/guides/integration/how-to-get-started https://github.com/buildnatively/js-sdk/releases
```

{% endtab %}
{% endtabs %}

### Setup Logic

{% tabs %}
{% tab title="Bubble.io Plugin" %}

### \[Element] Natively - Calendar

{% hint style="danger" %}
Set the element's data type field to **CalendarObject (Natively - ...)**.
{% endhint %}

<figure><img src="/files/jk6OZcNA1cPOdi9JltLV" alt=""><figcaption></figcaption></figure>

#### Events:

* **Get Calendars Success** - fires when calendars are successfully retrieved.
* **Get Calendars Failed** - fires if retrieving calendars fails.
* **Create Event Success** - fires when a new event is successfully created.
* **Create Event Failed** - fires if creating an event fails.

#### States:

* **Get Calendars Result** - list of `CalendarObject`, each with:
  * **id** - unique calendar identifier on the device.
* **Create Event Calendar ID** - the calendar ID where the new event was created.
* **Create Event/Get Calendars Status** - result status after calling **Create Event** or **Get Calendars**.
* **Event Title** - the title of the event that was created.
* **Event End Date** - the event's end date, in ISO 8601 format.
* **Event Start Date** - the event's start date, in ISO 8601 format.
* **Error** - error message, if any. Examples:
  * `add_calendar_event_failure` - something went wrong on the app side;
  * `start_date_missing` ;
  * `end_date_missing` ;
  * `no_available_calendars` - the user has no available calendars;
  * `cannot_retrieve_calendars` - something went wrong on the app side;
  * `calendar_permission_missing` - permission not granted;
  * `timezone_missing` ;
* **Message** - describes the current stage of the process, if applicable.

#### Actions:

* **Get Calendars** - returns a list of `CalendarObject`
* **Create Event** - returns the event's Calendar ID, Title, End Date, and Start Date:
  * **Title**;
  * **End Date**;
  * **Start Date**;
  * **Timezone** - ISO 8601 format;
  * **Description**.<br>
    {% endtab %}

{% tab title="JavaScript SDK" %}

```javascript
// ============================================================================
// NATIVELY CALENDARS - DOCUMENTATION & EXAMPLES
// ============================================================================

const calendar = new NativelyCalendar();

// ============================================================================
// ALL AVAILABLE METHODS
// ============================================================================
// calendar.retrieveCalendars(callback)
//   - Retrieves all calendars available on the device.
//
// calendar.createCalendarEvent(title, endDate, startDate, timezone, calendarId, description, callback)
//   - Creates a new event in the specified calendar.
//   - title: string
//   - endDate: Date — a JavaScript Date object, not a string
//   - startDate: Date — a JavaScript Date object, not a string
//   - timezone: string
//   - calendarId: string
//   - description: string

// ============================================================================
// CALLBACK RESPONSE FIELDS - retrieveCalendars
// ============================================================================
// resp.status: string — "SUCCESS" or "FAILED"
// resp.data: object — a map of calendar ID to calendar name,
//            e.g. { "1": "My calendar", "42": "email@address.com" }
// resp.error: string — error message, if any. Possible values:
//   - "no_available_calendars" — the user has no available calendars
//   - "cannot_retrieve_calendars" — something went wrong on the app side
//   - "calendar_permission_missing" — permission not granted

// ============================================================================
// CALLBACK RESPONSE FIELDS - createCalendarEvent
// ============================================================================
// resp.status: string — "SUCCESS" or "FAILED"
// resp.data.id: string — the calendar ID where the event was created
// resp.data.title: string — the event title
// resp.data.end: string — the event end date, in ISO 8601 format
// resp.data.start: string — the event start date, in ISO 8601 format
// resp.error: string — error message, if any. Possible values:
//   - "add_calendar_event_failure" — something went wrong on the app side
//   - "start_date_missing"
//   - "end_date_missing"
//   - "timezone_missing"
//   - "calendar_permission_missing" — permission not granted

// --- Calendars. Retrieve Calendars Example. Start ---

const retrieve_calendars_callback = function (resp) {
  if (resp.status === "FAILED") {
    console.log("Error:", resp.error); // e.g. "no_available_calendars", "calendar_permission_missing"
    return;
  }

  // resp.data is an object mapping calendar ID to calendar name
  // e.g. { "1": "My calendar", "42": "email@address.com" }
  for (const [calendarId, calendarName] of Object.entries(resp.data)) {
    console.log(calendarId, calendarName);
  }
};

calendar.retrieveCalendars(retrieve_calendars_callback);

// --- Calendars. Retrieve Calendars Example. End ---


// --- Calendars. Create Calendar Event Example. Start ---

const title = "Team Meeting";
const startDate = new Date("2025-07-10T14:00:00.000Z");
const endDate = new Date("2025-07-10T15:00:00.000Z");
const timezone = "Africa/Abidjan";
const calendarId = "calendar_id";
const description = "Event notes";

const create_calendar_event_callback = function (resp) {
  if (resp.status === "FAILED") {
    console.log("Error:", resp.error); // e.g. "start_date_missing", "timezone_missing"
    return;
  }

  console.log(resp.data.id);     // calendar ID
  console.log(resp.data.title);  // event title
  console.log(resp.data.end);    // event end date, ISO 8601
  console.log(resp.data.start);  // event start date, ISO 8601
};

calendar.createCalendarEvent(
  title,
  endDate,
  startDate,
  timezone,
  calendarId,
  description,
  create_calendar_event_callback
);

// --- Calendars. Create Calendar Event Example. End ---
```

{% endtab %}

{% tab title="AI Agents" %}
AI-powered editors like Lovable, Base44, and Replit use the JavaScript SDK to implement Natively features.&#x20;

Copy the line below and paste it into your AI agent.&#x20;

<pre><code>[<a data-footnote-ref href="#user-content-fn-1">Your feature description</a>] using the Natively Calendars SDK: const calendar = new NativelyCalendar(); calendar.retrieveCalendars(callback) retrieves all calendars available on the device, returning resp.status ("SUCCESS" or "FAILED"), resp.data (an object mapping calendar ID to calendar name, e.g. { "1": "My calendar", "42": "email@address.com" }), and resp.error ("no_available_calendars", "cannot_retrieve_calendars", or "calendar_permission_missing" if status is "FAILED"). calendar.createCalendarEvent(title, endDate, startDate, timezone, calendarId, description, callback) creates a new event — endDate and startDate must be JavaScript Date objects, not strings — returning resp.status ("SUCCESS" or "FAILED"), resp.data.id (calendar ID), resp.data.title, resp.data.end, resp.data.start (ISO 8601 dates), and resp.error ("start_date_missing", "end_date_missing", "timezone_missing", or "calendar_permission_missing" if status is "FAILED"). For reference: https://docs.buildnatively.com/guides/integration/calendars
</code></pre>

{% hint style="warning" %}
Replace the placeholder at the beginning with a description of what you want to build - for example: "Add a booking confirmation event to the user's calendar after checkout".
{% endhint %}
{% endtab %}
{% endtabs %}

### How to use

#### Check `resp.status` before using the result

Both `retrieveCalendars` and `createCalendarEvent` return a `status` field - always confirm it's `"SUCCESS"` before reading `resp.data`, and check `resp.error` when it's `"FAILED"` to handle the specific failure case.

#### Let the user pick a calendar before creating an event

Since a device may have multiple calendars, use `retrieveCalendars` first to show the user their available calendars (from the ID > name map), then pass the selected calendar's ID into `createCalendarEvent`.

#### Always construct real `Date` objects

`endDate` and `startDate` must be JavaScript `Date` instances, not date strings - pass a string and the method will throw. Use `new Date(...)` to construct them from your app's date data before calling `createCalendarEvent`.

#### Use this to confirm bookings or appointments

A common use case is adding a calendar event right after a booking or appointment is confirmed in your app, so the user doesn't have to manually add it themselves.

## Troubleshooting

<details>

<summary>Calendars permission denied</summary>

If the user previously denied calendar access, the feature can't prompt again automatically. Direct them to [Open App Settings](/guides/integration/open-app-settings.md) to manually re-enable the permission.

</details>

<details>

<summary><code>retrieveCalendars</code> returns <code>resp.error: "no_available_calendars"</code></summary>

The user's device has no calendars set up. There's nothing to select from until they add a calendar account on their device.

</details>

<details>

<summary>Not working in a web browser</summary>

This is a native feature and will not work in a standard web browser. Test on a real device using a Natively build or the Preview app.

</details>

[^1]: Replace this placeholder
