---
title: "Javascript Event calendar Public Google Calendar Integration Example | Mobiscroll"
component: "Event calendar"
framework: "JavaScript"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/javascript/eventcalendar/load-events-from-google-calendar"
---

# Public Google Calendar Integration - JavaScript Event calendar demo

Load events from a public Google Calendar into a Javascript event calendar using the integration plugin — provide your Calendar ID and API key. Plain JavaScript API.

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

## Description

Event data can be loaded from remote sources, like public google calendars. Through the [**integration plugin**](https://mobiscroll.com/docs/javascript/eventcalendar/calendar-integrations) you can easily show events that are available in a public calendar. The integration plugin needs to be added to the download package or installed separately from a dedicated NPM package.

You will just need the `CALENDAR_ID` and an `apiKey` from google and you should be ready to roll.

Public calendars are read-only. If you are interested in syncing private google calendars with read/write/delete access, [check out this example](https://demo.mobiscroll.com/javascript/eventcalendar/sync-events-google-calendar).

This example uses the [**integration plugin**](https://mobiscroll.com/docs/javascript/eventcalendar/calendar-integrations) to connect to and load events from Google Calendar 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 month-view event calendar with event labels loaded from public Google Calendars.
- **Custom header** The current month and year are shown on the left side of the header.
- **View switching** A segmented control in the center lets users switch between `Month`, `Week`, `Day`, and `Agenda` views, with `Month` selected by default.
- **Secondary navigation** Blue previous and next arrow buttons are shown on the right side of the header with a `Today` button between them.
- **Month view** Days with events display event labels inside the month grid cells.
- **Event selection** Hovering over or clicking an event label highlights the selected event.
- **Day cell states** Hovering a day cell highlights the day number with a gray background, while clicking the empty part of the cell selects the day and highlights the day number with a blue background.
- **Swipe navigation** The calendar supports changing months by clicking and dragging the view left or right.
- **Weekly scheduler view** Selecting the `Week` option from the segmented control switches the layout to a weekly scheduler view. This shows the days of the week at the top, an all-day section for all-day events, and a 24-hour time grid for each day of the week for timed events.
- **Daily scheduler view** Selecting the `Day` option from the segmented control switches the layout to a daily scheduler view. This shows the days of the week at the top, an all-day section for all-day events, and a 24-hour time grid for one day for timed events.
- **Scheduler view** The current time is marked with a blue line in the time grid.
- **Scheduler view** The time grid can be scrolled vertically with the mouse wheel while the header stays sticky.
- **Weekly agenda view** Selecting the `Agenda` option from the segmented control switches the layout to a weekly agenda view. This shows the days of the week at the top, and below the events listed in weekly agenda.

## Best for

- **Google Calendar overlays** Showing events from public Google Calendars inside a Mobiscroll month-view calendar.
- **Read-only event publishing** Sharing team, organization, or community calendars where visitors need to browse events but not edit them.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-themeVariant) - option
- [`onInit`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onInit) - event
- [`setEvents`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setEvents) - method
- [`view`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-view) - option
- [`labels`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-labels) - option
- [`exclusiveEndDates`](https://mobiscroll.com/docs/javascript/eventcalendar/api#opt-exclusiveEndDates) - option
- [`onPageLoading`](https://mobiscroll.com/docs/javascript/eventcalendar/api#event-onPageLoading) - event
- [`renderHeader`](https://mobiscroll.com/docs/javascript/eventcalendar/api#renderer-renderHeader) - renderer
- [`setOptions`](https://mobiscroll.com/docs/javascript/eventcalendar/api#method-setOptions) - method

## Code

### JS

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

var googleCalendarSync = mobiscroll.googleCalendarSync;
var CALENDAR_ID = 'theacidmedia.net_8l6v679q5j2f7q8lpmcjr4mm3k@group.calendar.google.com';
var cont = document.getElementById('demo-google-calendar-cont');
var view = 'month';
var firstDay;
var lastDay;

googleCalendarSync.init({
  apiKey: '<YOUR_GOOGLE_API_KEY>',
  onInit: loadEvents,
});

function loadEvents() {
  loadingEvents(true);
  googleCalendarSync
    .getEvents(CALENDAR_ID, firstDay, lastDay)
    .then(function (resp) {
      loadingEvents(false);
      calInst.setEvents(resp);
    })
    .catch(onError);
}

function loadingEvents(show) {
  if (show) {
    cont.classList.add('md-loading-events');
  } else {
    cont.classList.remove('md-loading-events');
  }
}

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

var calInst = mobiscroll.eventcalendar('#demo-google-cal', {
  view: {
    calendar: {
      labels: true,
    },
  },
  exclusiveEndDates: true,
  onPageLoading: function (args) {
    var start = args.viewStart;
    var end = args.viewEnd;

    // Calculate dates
    // (pre-load events for previous and next pages as well)
    if (view == 'month') {
      firstDay = start;
      lastDay = end;
    } else {
      firstDay = new Date(start.getFullYear(), start.getMonth(), start.getDate() - 7);
      lastDay = new Date(end.getFullYear(), end.getMonth(), end.getDate() + 7);
    }
  },
  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="google-cal-header-picker">' +
      '<label>Month<input mbsc-segmented type="radio" name="view" value="month" class="md-google-calendar-view" checked></label>' +
      '<label>Week<input mbsc-segmented type="radio" name="view" value="week" class="md-google-calendar-view"></label>' +
      '<label>Day<input mbsc-segmented type="radio" name="view" value="day" class="md-google-calendar-view"></label>' +
      '<label>Agenda<input mbsc-segmented type="radio" name="view" value="agenda" class="md-google-calendar-view"></label>' +
      '</div>' +
      '<div mbsc-calendar-prev class="google-cal-header-prev"></div>' +
      '<div mbsc-calendar-today class="google-cal-header-today"></div>' +
      '<div mbsc-calendar-next class="google-cal-header-next"></div>'
    );
  },
});

document.querySelectorAll('.md-google-calendar-view').forEach(function (elm) {
  elm.addEventListener('change', function (ev) {
    view = ev.target.value;
    switch (view) {
      case 'month':
        calInst.setOptions({
          view: {
            calendar: {
              labels: true,
            },
          },
        });
        break;
      case 'week':
        calInst.setOptions({
          view: {
            scheduler: { type: 'week' },
          },
        });
        break;
      case 'day':
        calInst.setOptions({
          view: {
            scheduler: { type: 'day' },
          },
        });
        break;
      case 'agenda':
        calInst.setOptions({
          view: {
            calendar: { type: 'week' },
            agenda: { type: 'week' },
          },
        });
        break;
    }
  });
});
```

### HTML

```html
<div id="demo-google-cal" class="md-google-calendar"></div>
```

### CSS

```css
.md-google-calendar .mbsc-segmented {
    max-width: 300px;
    margin: 0 auto;
}

.google-cal-header-picker {
    flex: 1 0 auto;
}

/* material header order */

.mbsc-material.google-cal-header-prev {
    order: 1;
}

.mbsc-material.google-cal-header-next {
    order: 2;
}

.mbsc-material.google-cal-header-nav {
    order: 3;
}

.mbsc-material .google-cal-header-picker {
    order: 4;
}

.mbsc-material .google-cal-header-today {
    order: 5;
}

/* windows header order */

.mbsc-windows.google-cal-header-nav {
    order: 1;
}

.mbsc-windows.google-cal-header-prev {
    order: 2;
}

.mbsc-windows.google-cal-header-next {
    order: 3;
}

.mbsc-windows .google-cal-header-picker {
    order: 4;
}

.mbsc-windows .google-cal-header-today {
    order: 5;
}

/* 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

- Import `googleCalendarSync` from `@mobiscroll/calendar-integration` (a separately installed package). Define a single `CALENDAR_ID` constant - one public Google Calendar. No user sign-in required; access is via API key only. Set `exclusiveEndDates: true`. The calendar is read-only (no `clickToCreate`/`dragToCreate`).
- Default view: `calendar: { labels: true }`. A `SegmentedGroup` view switcher in the header lets the user pick between four views:
  - `'month'` → `{ calendar: { labels: true } }`
  - `'week'` → `{ scheduler: { type: 'week' } }`
  - `'day'` → `{ scheduler: { type: 'day' } }`
  - `'agenda'` → `{ calendar: { type: 'week' }, agenda: { type: 'week' } }`
- Call `googleCalendarSync.init({ apiKey, onInit: loadEvents })` on mount. `onInit` fires when the integration is ready.
- **`loadEvents`**: calls `googleCalendarSync.getEvents(CALENDAR_ID, firstDay.current, lastDay.current)` and replaces the events array on success.
- **`onPageLoading`**: sets date refs and calls `loadEvents()`. For `'month'` view use `args.viewStart`/`args.viewEnd` directly. For all other views, pre-load ±7 days: `firstDay = start − 7 days`, `lastDay = end + 7 days`.
- **Custom header** (`renderHeader`, Angular: `headerTemplate`, Vue: `#header` slot): `CalendarNav` (left), spinner (visible while loading), `SegmentedGroup` (Month/Week/Day/Agenda), 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/javascript/getting-started/installation

## Related demos

- Previous: [Outlook Integration](https://demo.mobiscroll.com/javascript/eventcalendar/sync-events-outlook-calendar.md) - Connect the Javascript event calendar to Outlook via plugin — sign in users, load calendars, and add, edit, or delete events with optional write-back.
