---
title: "JQuery Scheduler Outlook Calendar Integration Example | Mobiscroll"
component: "Scheduler"
framework: "jQuery"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/jquery/scheduler/sync-events-outlook-calendar"
---

# Outlook Calendar Integration - jQuery Scheduler demo

Connect Outlook Calendar to a JQuery Scheduler — authenticate with Microsoft sign-in, load Outlook events, and sync add, edit, and delete actions.

- [Live demo](https://demo.mobiscroll.com/jquery/scheduler/sync-events-outlook-calendar)
- [View code on GitHub](https://github.com/acidb/mobiscroll-demos-jquery/tree/main/src/demos/eventcalendar/scheduler/sync-events-outlook-calendar)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/scheduler/sync-events-outlook-calendar.md), [Angular](https://demo.mobiscroll.com/angular/scheduler/sync-events-outlook-calendar.md), [React](https://demo.mobiscroll.com/react/scheduler/sync-events-outlook-calendar.md), [Vue](https://demo.mobiscroll.com/vue/scheduler/sync-events-outlook-calendar.md)

## Description

The event calendar comes with third party calendar integration support through the [**integration plugin**](https://mobiscroll.com/docs/jquery/eventcalendar/calendar-integrations). This needs to be added to the download package or installed separately from a dedicated NPM package.

It includes everything you need to authenticate your users, get their Outlook calendars and provides functions for CRUD operations. It takes care of loading the events and converts them to the required format so that they can be displayed on the Mobiscroll calendar separately or mixed with other calendars and events.

Customizing the interaction, event workflows, whether the events are read-only is up to you. In this live demo, newly created events will land in the `primary calendar` of the authenticated user.

This example uses the [**integration plugin**](https://mobiscroll.com/docs/jquery/eventcalendar/calendar-integrations) to connect to and load events from Outlook Calendars and it is not available in the trial. Give the live demo a try and see how it can be configured.

## What this demo shows

- A desktop weekly scheduler layout with a fixed week strip at the top, a fixed all-day row below it, and a scrollable scheduler time grid for the selected week.
- **Initial state** The scheduler view is visible with no events loaded by default.
- **Side panel** A separate panel on the left invites the user to sign in with the text `Log into your Outlook account to view and edit your Outlook Calendar events` and a `Sync My Outlook Calendar` button.
- **Authentication flow** Clicking `Sync My Outlook Calendar` opens a new browser tab where the user signs in to their Microsoft account.
- **Connected state** After sign-in, the side panel updates to show the text `Editing events sync back to your calendar when enabled. You'll be asked for confirmation on every action.`
- **Editing toggle** The connected panel includes an `Enable editing` switch that is off by default.
- **Editing toggle enabled** Editing and event opens a popup with the following message `Are you sure you want to update this event? This actin will affect your Google Calendar event.` and below that ther is a `Cancel` and `Update` button, by clicking the firs one, the popup closes and the event editing won't happen and by clicking the second button will update the event on Mobiscroll scheduler and sync back to the Outlook Calendar as well. 
- **Event creation or update** If a new event is created or an existing one updated, a `Event created/updated/ deleted in [the name of your Outlook Calendar] calendar` toast appears at the bottom center of the scheduler.
- **Calendar selection** The connected panel includes a `My Calendars` section with separate switches for the available Outlook calendars.
- **Sign-out** The bottom of the side panel includes a `Log Out Of My Account` button for disconnecting the Outlook account.
- **Header navigation** The month and year label in the top left opens date navigation, while the blue previous and next arrows and the Today button on the right make it easy to move between weeks and jump back to the current day.
- **Week view** The fixed strip below the header displays the days of the selected week, with the current date highlighted by a blue circle.
- **All-day events** All-day events appear in a dedicated row that stays fixed above the time grid.
- **Time grid** The scheduler grid scrolls vertically through the hours of the selected week.
- **Timed events** Once Outlook events are loaded, timed events are displayed in the weekly scheduler grid as colored cards with a colored stripe on the left, the event title in bold, and the exact start and end time displayed above the title.
- **Current time** A blue current-time line appears across the time grid, with a small blue dot marking the current date.
- **Hover behavior** Hovering over the time grid shows a time indicator that follows the cursor in 15-minute increments.
- **Event interaction** Hovering over events highlights them and shows drag and resize handles for repositioning or changing duration.
- **Event selection** Clicking an event selects and highlights it.
- **Event creation** Double-clicking the time grid or clicking and dragging on it creates a new event.

## Best for

- **User-controlled calendar sync** Experiences where users need to connect their own Outlook account, choose which calendars to display, and disconnect when needed.
- **Optional write-back workflows** Scenarios where external calendar events should stay read-only by default, with editing enabled only when the user explicitly turns it on.
- **Multi-calendar visibility** Interfaces that need to surface multiple Outlook calendars, such as personal calendars, shared calendars, or regional holiday calendars, in a single month view.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-themeVariant) - option
- [`setEvents`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setEvents) - method
- [`view`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-view) - option
- [`extendDefaultEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-extendDefaultEvent) - option
- [`exclusiveEndDates`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-exclusiveEndDates) - option
- [`clickToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/jquery/eventcalendar/api#opt-dragToResize) - option
- [`renderHeader`](https://mobiscroll.com/docs/jquery/eventcalendar/api#renderer-renderHeader) - renderer
- [`onPageLoading`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onPageLoading) - event
- [`onEventCreate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventCreate) - event
- [`removeEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-removeEvent) - method
- [`addEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-addEvent) - method
- [`onEventUpdate`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventUpdate) - event
- [`updateEvent`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-updateEvent) - method
- [`onEventDelete`](https://mobiscroll.com/docs/jquery/eventcalendar/api#event-onEventDelete) - event
- [`setOptions`](https://mobiscroll.com/docs/jquery/eventcalendar/api#method-setOptions) - method

## Code

### JS

```js
mobiscroll.setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

$(function () {
  var outlookCalendarSync = mobiscroll.outlookCalendarSync;

  var $loginButton = $('#demo-outlook-cal-sign-in');
  var $logoutButton = $('#demo-outlook-cal-sign-out');

  var $calendarList = $('#demo-outlook-cal-list');
  var $loggedOutCont = $('#demo-logged-out-cont');
  var $loggedInCont = $('#demo-logged-in-cont');
  var $cont = $('#demo-outlook-calendar-cont');
  var $editButton = $('#demo-outlook-cal-edit');

  var startDate;
  var endDate;
  var calendarIds = [];
  var events = [];
  var calendarData = {};
  var primaryCalendarId;
  var debounce;

  function toggleContainers(loggedIn) {
    if (loggedIn) {
      $loggedOutCont.hide();
      $loggedInCont.show();
    } else {
      $loggedInCont.hide();
      $loggedOutCont.show();
    }
  }

  function loadEvents(checked, calendarId) {
    var oldEvents = events;
    if (checked) {
      loadingEvents(true);
      calendarIds.push(calendarId);
      outlookCalendarSync
        .getEvents([calendarId], startDate, endDate)
        .then(function (resp) {
          loadingEvents(false);
          events = events.concat(resp);
          inst.setEvents(events);
        })
        .catch(onError);
    } else {
      var index = calendarIds.indexOf(calendarId);
      if (index !== -1) {
        calendarIds.splice(index, 1);
      }
      events = oldEvents.filter(function (event) {
        return event.outlookCalendarId !== calendarId;
      });
      inst.setEvents(events);
    }
  }

  function onError(resp) {
    mobiscroll.toast({
      message: resp.message,
    });
  }

  function onSignedIn() {
    toggleContainers(true);
    outlookCalendarSync.getCalendars().then(function (calendars) {
      var calList = '<div class="mbsc-form-group-title">My Calendars</div>';

      calendars.sort(function (c) {
        return c.isDefaultCalendar ? -1 : 1;
      });
      primaryCalendarId = calendars[0].id;
      calendarIds.push(primaryCalendarId);

      for (var i = 0; i < calendars.length; ++i) {
        var c = calendars[i];
        calList +=
          '<label>' +
          '<input mbsc-switch id="' +
          i +
          '" value="' +
          c.id +
          '" data-label="' +
          c.name +
          '" type="checkbox" class="outlook-calendar-switch"' +
          (i == 0 ? 'checked' : '') +
          '/>' +
          '</label>';

        calendarData[c.id] = { name: c.name, color: c.hexColor };
      }

      $calendarList.append(calList);
      mobiscroll.enhance($calendarList[0]);

      loadingEvents(true);

      outlookCalendarSync
        .getEvents([calendars[0].id], startDate, endDate)
        .then(function (resp) {
          loadingEvents(false);
          events = resp;
          inst.setEvents(events);
        })
        .catch(onError);
    });
  }

  function onSignedOut() {
    toggleContainers(false);
    calendarIds = [];
    calendarData = {};
    events = [];
    inst.setEvents([]);
    $calendarList.empty();
  }

  function loadingEvents(show) {
    if (show) {
      $cont.addClass('md-loading-events');
    } else {
      $cont.removeClass('md-loading-events');
    }
  }

  var inst = $('#demo-outlook-calendar')
    .mobiscroll()
    .eventcalendar({
      view: {
        scheduler: { type: 'week' },
      },
      extendDefaultEvent: function () {
        if (primaryCalendarId) {
          return {
            color: calendarData[primaryCalendarId].color,
          };
        }
      },
      exclusiveEndDates: true,
      clickToCreate: false,
      dragToCreate: false,
      dragToMove: false,
      dragToResize: false,
      renderHeader: function () {
        return (
          '<div mbsc-calendar-nav></div>' +
          '<div class="md-spinner">' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '<div class="md-spinner-blade"></div>' +
          '</div>' +
          '<div class="md-outlook-calendar-header">' +
          '<button mbsc-calendar-prev></button>' +
          '<button mbsc-calendar-today>Today</button>' +
          '<button mbsc-calendar-next></button></div>'
        );
      },
      onPageLoading: function (args) {
        startDate = args.viewStart;
        endDate = args.viewEnd;
        clearTimeout(debounce);
        debounce = setTimeout(function () {
          if (outlookCalendarSync.isSignedIn()) {
            loadingEvents(true);
            outlookCalendarSync
              .getEvents(calendarIds, startDate, endDate)
              .then(function (resp) {
                loadingEvents(false);
                events = resp;
                inst.setEvents(events);
              })
              .catch(onError);
          }
        }, 200);
      },
      onEventCreate: function (args) {
        if (outlookCalendarSync.isSignedIn()) {
          var event = args.event;
          outlookCalendarSync
            .addEvent(primaryCalendarId, event)
            .then(function (resp) {
              inst.removeEvent(event);
              resp.color = event.color;
              inst.addEvent(resp);
              events.push(resp);
              mobiscroll.toast({
                message: 'Event created in "' + calendarData[primaryCalendarId].name + '" calendar',
              });
            })
            .catch(function (error) {
              inst.removeEvent(args.event);
              onError(error);
            });
        }
      },
      onEventUpdate: function (args) {
        if (outlookCalendarSync.isSignedIn()) {
          mobiscroll
            .confirm({
              title: 'Are you sure you want to update this event?',
              message: 'This action will affect your Outlook Calendar event.',
              okText: 'Update',
            })
            .then(function (result) {
              if (result) {
                var event = args.event;
                var calendarId = event.outlookCalendarId;
                outlookCalendarSync
                  .updateEvent(calendarId, event)
                  .then(function () {
                    mobiscroll.toast({
                      message: 'Event updated on "' + calendarData[calendarId].name + '" calendar',
                    });
                  })
                  .catch(function (error) {
                    inst.updateEvent(args.oldEvent);
                    onError(error);
                  });
              } else {
                inst.updateEvent(args.oldEvent);
              }
            });
        }
      },
      onEventDelete: function (args) {
        if (outlookCalendarSync.isSignedIn()) {
          mobiscroll
            .confirm({
              title: 'Are you sure you want to delete this event?',
              message: 'This action will remove the event from your Outlook Calendar as well.',
              okText: 'Delete',
            })
            .then(function (result) {
              if (result) {
                var event = args.event;
                var calendarId = event.outlookCalendarId;
                outlookCalendarSync
                  .deleteEvent(calendarId, event)
                  .then(function () {
                    inst.removeEvent(event);
                    events = events.filter(function (ev) {
                      return event.id !== ev.id;
                    });
                    mobiscroll.toast({
                      message: 'Event deleted from "' + calendarData[calendarId].name + '" calendar',
                    });
                  })
                  .catch(onError);
              }
            });
        }
        return false;
      },
    })
    .mobiscroll('getInst');

  // Sign in
  $loginButton.on('click', function () {
    if (!outlookCalendarSync.isSignedIn()) {
      outlookCalendarSync.signIn().catch(onError);
    }
  });

  // Switch click
  $loggedInCont.on('change', '.outlook-calendar-switch', function (ev) {
    loadEvents(ev.target.checked, ev.target.value);
  });

  // Edit click
  $editButton.on('change', function (ev) {
    var isEditable = ev.target.checked;
    inst.setOptions({
      clickToCreate: isEditable,
      dragToCreate: isEditable,
      dragToMove: isEditable,
      dragToResize: isEditable,
    });
  });

  // Sign out
  $logoutButton.on('click', function () {
    outlookCalendarSync.signOut().catch(onError);
  });

  // Init client
  outlookCalendarSync.init({
    clientId: '<YOUR_OUTLOOK_CLIENT_ID>',
    redirectUri: '<YOUR_OUTLOOK_REDIRECT_URI>',
    onSignedIn: onSignedIn,
    onSignedOut: onSignedOut,
  });
});
```

### HTML

```html
<div mbsc-page class="md-sync-events-outlook-cont">
    <div class="md-sync-events-outlook-menu">
        <div id="demo-logged-out-cont">
            <div class="mbsc-form-group-inset mbsc-align-center">
                <p class="mbsc-italic mbsc-txt-muted">Log into your Outlook account to view and edit your Outlook Calendar events</p>
                <button mbsc-button id="demo-outlook-cal-sign-in" class="md-sync-events-outlook-button mbsc-button-block">Sync my outlook calendars</button>
            </div>
        </div>
        <div id="demo-logged-in-cont" style="display:none">
            <div class="mbsc-form-group-inset mbsc-align-center">
                <p class="mbsc-italic mbsc-txt-muted">Editing events sync back to your calendar when enabled. You'll be asked for confirmation on every action.</p>
            </div>
            <div class="mbsc-form-group-inset">
                <label>
                    <input id="demo-outlook-cal-edit" mbsc-switch data-label="Enable editing" />
                </label>
            </div>
            <div id="demo-outlook-cal-list" class="mbsc-form-group-inset md-sync-events-outlook-inset"></div>
            <div class="mbsc-form-group-inset">
                <button mbsc-button id="demo-outlook-cal-sign-out" class="md-sync-events-outlook-button mbsc-button-block">Log out of my account</button>
            </div>
        </div>
    </div>
    <div id="demo-outlook-calendar-cont" class="md-sync-events-outlook-calendar">
        <div class="md-sync-events-overlay"></div>
        <div id="demo-outlook-calendar"></div>
    </div>
</div>
```

### CSS

```css
.md-sync-events-outlook-cont.mbsc-page {
    display: flex;
    height: 600px;
}

.md-sync-events-outlook-menu {
    flex: 0 0 350px;
    height: 100%;
    overflow: auto;
}

.md-sync-events-outlook-calendar {
    flex: 1 1 auto;
    border-left: 1px solid #ccc;
}

.md-outlook-calendar-header {
    flex: 1 0 auto;
    display: flex;
    justify-content: flex-end;
}

.md-sync-events-outlook-button.mbsc-button {
    text-transform: capitalize;
}

.md-sync-events-outlook-inset {
    margin-bottom: 0;
}

@media (max-width: 800px) {
    .md-sync-events-outlook-cont.mbsc-page {
        display: block;
        height: auto;
    }
}

/* loading spinner and overlay */

.md-loading-events .md-sync-events-overlay {
    position: absolute;
    z-index: 2;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
}

.md-spinner {
    visibility: hidden;
    position: relative;
    width: 20px;
    height: 20px;
}

.md-loading-events .md-spinner {
    visibility: visible;
}

.md-spinner .md-spinner-blade {
    position: absolute;
    left: 44.5%;
    top: 37%;
    width: 10%;
    height: 25%;
    border-radius: 50%/20%;
    background-color: #8C8C8C;
    -webkit-animation: md-spinner-fade 1s linear infinite;
    animation: md-spinner-fade 1s linear infinite;
    -webkit-animation-play-state: paused;
    animation-play-state: paused;
}

.md-spinner .md-spinner-blade:nth-child(1) {
    -webkit-animation-delay: -1.66667s;
    animation-delay: -1.66667s;
    -webkit-transform: rotate(30deg) translate(0, -150%);
    transform: rotate(30deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(2) {
    -webkit-animation-delay: -1.58333s;
    animation-delay: -1.58333s;
    -webkit-transform: rotate(60deg) translate(0, -150%);
    transform: rotate(60deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(3) {
    -webkit-animation-delay: -1.5s;
    animation-delay: -1.5s;
    -webkit-transform: rotate(90deg) translate(0, -150%);
    transform: rotate(90deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(4) {
    -webkit-animation-delay: -1.41667s;
    animation-delay: -1.41667s;
    -webkit-transform: rotate(120deg) translate(0, -150%);
    transform: rotate(120deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(5) {
    -webkit-animation-delay: -1.33333s;
    animation-delay: -1.33333s;
    -webkit-transform: rotate(150deg) translate(0, -150%);
    transform: rotate(150deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(6) {
    -webkit-animation-delay: -1.25s;
    animation-delay: -1.25s;
    -webkit-transform: rotate(180deg) translate(0, -150%);
    transform: rotate(180deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(7) {
    -webkit-animation-delay: -1.16667s;
    animation-delay: -1.16667s;
    -webkit-transform: rotate(210deg) translate(0, -150%);
    transform: rotate(210deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(8) {
    -webkit-animation-delay: -1.08333s;
    animation-delay: -1.08333s;
    -webkit-transform: rotate(240deg) translate(0, -150%);
    transform: rotate(240deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(9) {
    -webkit-animation-delay: -1s;
    animation-delay: -1s;
    -webkit-transform: rotate(270deg) translate(0, -150%);
    transform: rotate(270deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(10) {
    -webkit-animation-delay: -0.91667s;
    animation-delay: -0.91667s;
    -webkit-transform: rotate(300deg) translate(0, -150%);
    transform: rotate(300deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(11) {
    -webkit-animation-delay: -0.83333s;
    animation-delay: -0.83333s;
    -webkit-transform: rotate(330deg) translate(0, -150%);
    transform: rotate(330deg) translate(0, -150%);
}

.md-spinner .md-spinner-blade:nth-child(12) {
    -webkit-animation-delay: -0.75s;
    animation-delay: -0.75s;
    -webkit-transform: rotate(360deg) translate(0, -150%);
    transform: rotate(360deg) translate(0, -150%);
}

.md-loading-events .md-spinner-blade {
    -webkit-animation-play-state: running;
    animation-play-state: running;
}

@-webkit-keyframes md-spinner-fade {
    0% {
        opacity: 0.85;
    }
    50% {
        opacity: 0.25;
    }
    100% {
        opacity: 0.25;
    }
}

@keyframes md-spinner-fade {
    0% {
        opacity: 0.85;
    }
    50% {
        opacity: 0.25;
    }
    100% {
        opacity: 0.25;
    }
}
```

## Implementation notes

- Use `view: { scheduler: { type: 'week' } }` with `exclusiveEndDates: true`. Start with empty `data` - no resources are used; all calendar events appear in a single view.
- Import `outlookCalendarSync` from `@mobiscroll/calendar-integration` (a separately installed package).
- Call `outlookCalendarSync.init()` on mount with `clientId`, `redirectUri`, `onSignedIn`, and `onSignedOut`.
- **`onSignedIn`**: call `outlookCalendarSync.getCalendars()` and sort so the default calendar appears first (`c.isDefaultCalendar ? -1 : 1`). Build a `calendarData` map keyed by `c.id` storing `name` (`c.name`), `color` (`c.hexColor`), and `checked`. Set `calendarIds` to `[primaryCalId]` initially, then call `getEvents([primaryCalId], startDate, endDate)`. Angular: wrap `.then()` callbacks in `NgZone.run()`.
- **`onSignedOut`**: clear events, calendarIds, calendarData, and calendars list.
- **`onPageLoading`**: debounce by 200ms; if `outlookCalendarSync.isSignedIn()`, call `getEvents(calendarIds, args.viewStart, args.viewEnd)` and replace events. Store `args.viewStart`/`args.viewEnd` in refs for use in toggle handlers.
- **Toggle calendar** (per-calendar Switch in sidebar): enabling - push to `calendarIds`, call `getEvents([calendarId], startDate, endDate)`, append returned events; disabling - filter calendarId and events out by `event.outlookCalendarId`.
- Set `extendDefaultEvent` to return `{ color: calendarData[primaryCalendarId].color }` so newly created events use the primary calendar's color.
- **CRUD** - all four options (`clickToCreate`, `dragToCreate`, `dragToMove`, `dragToResize`) controlled by an "Enable editing" Switch (off by default):
  - **Create** (`onEventCreate`): call `outlookCalendarSync.addEvent(primaryCalendarId, event)`. On success, set the new event's color and append it; on failure, show an error toast.
  - **Update** (`onEventUpdate`): `Confirm` dialog ("Are you sure you want to update this event?" / "This action will affect your Outlook Calendar event." / "Update") → `outlookCalendarSync.updateEvent(event.outlookCalendarId, event)`. On cancel or failure, restore `args.oldEvent`.
  - **Delete** (`onEventDelete`): return `false`, `Confirm` dialog ("Delete") → `outlookCalendarSync.deleteEvent(event.outlookCalendarId, event)`, filter the event out on success.
- **Layout**: a persistent sidebar panel alongside the calendar. When not logged in: "Sync my outlook calendars" sign-in button. When logged in: editing note, "Enable editing" Switch, per-calendar Switches (label: `cal.name`), "Log out" button.
- **Custom header** (`renderHeader`, Angular: `headerTemplate`, Vue: `#header` slot): `CalendarNav` (left), spinner (12 blade divs), then `CalendarPrev`, `CalendarToday`, `CalendarNext` (right).

## Run it in your project

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/jquery/getting-started/installation

## Related demos

- Previous: [Google Calendar Integration](https://demo.mobiscroll.com/jquery/scheduler/sync-events-google-calendar.md) - Connect the JQuery Scheduler to Google Calendar — authenticate users, load calendars, and sync create, edit, or delete events in real time via the integration plugin.
- Next: [Google Calendar Integration](https://demo.mobiscroll.com/jquery/scheduler/load-events-from-google-calendar.md) - Load public Google Calendar events into a JQuery Scheduler — connect with your calendar ID and API key using the Google Calendar integration plugin.
