# Vanilla JavaScript

> FullCalendar v7 documentation. Web version: https://fullcalendar.io/docs/vanilla-js

FullCalendar can be used without a framework. You create a `Calendar` object, hand it a DOM element and an object of options, and call its methods directly. The Vue, Angular and Web Component packages are wrappers around this same API. The React and Preact packages are not: they are FullCalendar written in those libraries, accepting the same options.

This package is released under an MIT license. Useful links:

- [NPM package](https://www.npmjs.com/package/fullcalendar)
- [Bug report instructions](https://fullcalendar.io/reporting-bugs)
- Example projects:
  - Standard: [Vite + TypeScript](https://github.com/fullcalendar/fullcalendar-examples/tree/main/typescript) | [Rollup](https://github.com/fullcalendar/fullcalendar-examples/tree/main/rollup) | [Parcel](https://github.com/fullcalendar/fullcalendar-examples/tree/main/parcel)
  - Scheduler: [Vite + TypeScript](https://github.com/fullcalendar/fullcalendar-examples/tree/main/typescript-scheduler) | [Rollup](https://github.com/fullcalendar/fullcalendar-examples/tree/main/rollup-scheduler)

This guide does not go into depth about setting up a build system. Please consult the aforementioned example projects for that.

## Installation

There are three ways to get FullCalendar onto the page. Whichever you use, you'll need to choose one of the [stock themes](https://fullcalendar.io/docs/stock-themes.md) (see them [live](https://fullcalendar.io/demos)), along with a [color palette](https://fullcalendar.io/docs/color-palettes.md). The examples below show a simplified way of importing the color palette (ex: `purple.css`), but please be aware, there are better ways to integrate with [dark mode](https://fullcalendar.io/docs/color-palettes.md#dark-mode).

### Via NPM

For non-trivial projects, it is recommended to use a build system like [Vite](https://vite.dev/) or [Rollup](https://rollupjs.org). It compiles everything into a unified bundle, so you won't need to manage `<script>` tags yourself.

Install the FullCalendar-related dependencies:

```bash
npm install --save fullcalendar temporal-polyfill
```

The `temporal-polyfill` package is small and tree-shakable, and can be used as a global polyfill if you explicitly opt-in ([more info](https://fullcalendar.io/docs/temporal-polyfill.md)).

FullCalendar's functionality is broken up into "plugins" ([see a full list](https://fullcalendar.io/docs/plugin-index.md)). A plugin you don't import is never compiled into your bundle. Import the ones you need and supply them to a new `Calendar` instance:

```js
import { Calendar } from 'fullcalendar';
import themePlugin from 'fullcalendar/themes/monarch'; // YOUR THEME
import dayGridPlugin from 'fullcalendar/daygrid';
import timeGridPlugin from 'fullcalendar/timegrid';
import listPlugin from 'fullcalendar/list';

// stylesheets
import 'fullcalendar/skeleton.css'; // ALWAYS NEED SKELETON
import 'fullcalendar/themes/monarch/theme.css'; // YOUR THEME
import 'fullcalendar/themes/monarch/palettes/purple.css'; // YOUR THEME'S PALETTE

const calendarEl = document.getElementById('calendar');
const calendar = new Calendar(calendarEl, {
  plugins: [themePlugin, dayGridPlugin, timeGridPlugin, listPlugin],
  initialView: 'dayGridMonth',
  headerToolbar: {
    left: 'prev,next today',
    center: 'title',
    right: 'dayGridMonth,timeGridWeek,listWeek'
  }
});
calendar.render();
```

You must initialize your calendar with at least one plugin that provides a view!

Example repos:

- [View the **Vite** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/typescript)
- [View the **Rollup** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/rollup)
- [View the **Parcel** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/parcel)

### Via Script Tags

It's possible to include `<script>` tags in the head of your HTML page and initialize a calendar via browser globals, using one of FullCalendar's prebuilt bundles. Obtain the standard `fullcalendar` bundle in one of the following ways:

- **Download**: <a href='https://github.com/fullcalendar/fullcalendar/releases/download/v7.1.0/fullcalendar-7.1.0.zip'>fullcalendar-7.1.0.zip</a>
- **CDN:** [jsdelivr](https://www.jsdelivr.com/package/npm/fullcalendar?version=7.1.0)
- **NPM:** `npm install fullcalendar`

Then, write the following initialization code:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <!-- STANDARD JS -->
    <script src="https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/all/global.js"></script>

    <!-- THEME JS -->
    <script src="https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/global.js"></script>

    <!-- STYLESHEETS -->
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/skeleton.css' rel='stylesheet' />
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/theme.css' rel='stylesheet' />
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/palettes/purple.css' rel='stylesheet' />

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const calendarEl = document.getElementById("calendar");
        const calendar = new FullCalendar.Calendar(calendarEl, {
          initialView: "dayGridMonth",
        });
        calendar.render();
      });
    </script>
  </head>
  <body>
    <div id="calendar"></div>
  </body>
</html>
```

[View a runnable example →](https://fullcalendar.io/docs/initialize-globals-demo)

With globals there is no `plugins` array to write. The bundle's `all/global(.min).js` file registers the following plugins for you:

- `interaction` (for [date selecting](https://fullcalendar.io/docs/date-clicking-selecting.md), [event dragging & resizing](https://fullcalendar.io/docs/event-dragging-resizing.md))
- `daygrid` (for [month](https://fullcalendar.io/docs/month-view.md) and [dayGrid](https://fullcalendar.io/docs/daygrid-view.md) views)
- `timegrid` (for [timeGrid](https://fullcalendar.io/docs/timegrid-view.md) views)
- `list` (for [list views](https://fullcalendar.io/docs/list-view.md))
- `multimonth` (for [multi-month views](https://fullcalendar.io/docs/multimonth-grid.md))

The [supplemental plugins](https://fullcalendar.io/docs/plugin-index.md) are not in the bundle: [Google Calendar](https://fullcalendar.io/docs/google-calendar.md), [iCalendar](https://fullcalendar.io/docs/icalendar.md), [RRule](https://fullcalendar.io/docs/rrule-plugin.md), [Moment formatting](https://fullcalendar.io/docs/format-moment.md) and [Luxon formatting](https://fullcalendar.io/docs/format-luxon3.md). Each ships its own `global.js`, which registers itself when loaded after `all/global.js`. Some also need their third-party library loaded first. See each plugin's page for its script tags.

### Via Script Tags (ESM)

FullCalendar can also be used as an ES module within a `<script type="module">` tag, with [import maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script/type/importmap) resolving the package names. Unlike the globals technique, you import and supply each plugin yourself:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />

    <!-- STYLESHEETS -->
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/skeleton.css' rel='stylesheet' />
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/theme.css' rel='stylesheet' />
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/palettes/purple.css' rel='stylesheet' />

    <script type="importmap">
      {
        "imports": {
          "preact": "https://cdn.jsdelivr.net/npm/preact@10.29.1/dist/preact.mjs",
          "preact/hooks": "https://cdn.jsdelivr.net/npm/preact@10.29.1/hooks/dist/hooks.mjs",
          "preact/jsx-runtime": "https://cdn.jsdelivr.net/npm/preact@10.29.1/jsx-runtime/dist/jsxRuntime.mjs",
          "preact/compat": "https://cdn.jsdelivr.net/npm/preact@10.29.1/compat/dist/compat.mjs",
          "preact/compat/client": "https://cdn.jsdelivr.net/npm/preact@10.29.1/compat/client.mjs",
          "temporal-polyfill/fns/ZonedDateTime": "https://cdn.jsdelivr.net/npm/temporal-polyfill@1.0.1/fns/ZonedDateTime.js",
          "temporal-polyfill/fns/PlainDateTime": "https://cdn.jsdelivr.net/npm/temporal-polyfill@1.0.1/fns/PlainDateTime.js",
          "temporal-polyfill/fns/Instant": "https://cdn.jsdelivr.net/npm/temporal-polyfill@1.0.1/fns/Instant.js",
          "@full-ui/headless-calendar": "https://cdn.jsdelivr.net/npm/@full-ui/headless-calendar@7.1.0/index.js",
          "fullcalendar": "https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/index.js",
          "fullcalendar/themes/monarch": "https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/index.js",
          "fullcalendar/interaction": "https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/interaction.js",
          "fullcalendar/daygrid": "https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/daygrid.js",
          "fullcalendar/timegrid": "https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/timegrid.js",
          "fullcalendar/list": "https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/list.js"
        }
      }
    </script>
    <script type="module">
      import { Calendar } from "fullcalendar";
      import themePlugin from "fullcalendar/themes/monarch";
      import dayGridPlugin from "fullcalendar/daygrid";
      import timeGridPlugin from "fullcalendar/timegrid";
      import listPlugin from "fullcalendar/list";

      document.addEventListener("DOMContentLoaded", function () {
        const calendarEl = document.getElementById("calendar");
        const calendar = new Calendar(calendarEl, {
          plugins: [themePlugin, dayGridPlugin, timeGridPlugin, listPlugin],
          initialView: "dayGridMonth",
          headerToolbar: {
            left: "prev,next today",
            center: "title",
            right: "dayGridMonth,timeGridWeek,timeGridDay,listWeek",
          },
        });
        calendar.render();
      });
    </script>
  </head>
  <body>
    <div id="calendar"></div>
  </body>
</html>
```

A [supplemental plugin](https://fullcalendar.io/docs/plugin-index.md) such as [RRule](https://fullcalendar.io/docs/rrule-plugin.md) works the same way: import it and add it to `plugins`. The import map needs an entry for the plugin's package and for each module it imports, such as `@fullcalendar/core/protected-api` and the third-party library it wraps.

## Options

The second argument to `new Calendar` is an object of options. It accepts [all of FullCalendar's options](https://fullcalendar.io/docs/all). Example:

```js
const calendar = new Calendar(calendarEl, {
  plugins: [dayGridPlugin],
  initialView: 'dayGridMonth',
  weekends: false,
  events: [
    { title: 'event 1', date: '2026-04-01' },
    { title: 'event 2', date: '2026-04-02' },
  ]
});
```

Options can also be scoped to a single view, or to a type of view, through the `views` option. See [View-Specific Options](https://fullcalendar.io/docs/view-specific-options.md).

## Handlers

A handler is an option whose value is a function that is called when something happens. For example, the [dateClick](https://fullcalendar.io/docs/dateClick.md) handler is called whenever the user clicks on a date:

```js
const calendar = new Calendar(calendarEl, {
  plugins: [dayGridPlugin, interactionPlugin],
  dateClick: function(info) {
    alert(info.dateStr);
  }
});
```

Handlers can also be attached and detached after initialization with the `on` and `off` methods. See [Handlers](https://fullcalendar.io/docs/handlers.md).

## Modifying Options

An option can be read or changed after the calendar has been initialized:

```js
calendar.getOption('weekends'); // false
calendar.setOption('weekends', true);
```

The calendar rerenders as needed. A few options can't be set this way and have a dedicated method instead. See [Get/Set Options Dynamically](https://fullcalendar.io/docs/dynamic-options.md).

## Content Injection

There are many settings throughout the API for injecting custom content, like the `eventContent` [event render hook](https://fullcalendar.io/docs/event-render-hooks.md). In vanilla JS, such a setting accepts content in the following formats.

**Unescaped text**, provided as a string:

```js
eventContent: 'some text'
```

**HTML**, wrapped in an object:

```js
eventContent: { html: '<i>some html</i>' }
```

**An array of DOM nodes**, wrapped in an object:

```js
eventContent: { domNodes: arrayOfDomNodes }
```

**A function** that returns any of the above formats:

```js
eventContent: function(info) {
  const italicEl = document.createElement('i')

  if (info.event.extendedProps.isUrgent) {
    italicEl.innerHTML = 'urgent event'
  } else {
    italicEl.innerHTML = 'normal event'
  }

  return { domNodes: [italicEl] }
}
```

Each setting is given different arguments. For example, `eventContent` receives a single object [with a whole bunch of properties](https://fullcalendar.io/docs/event-render-hooks.md#argument).

### Virtual DOM

FullCalendar internally uses a virtual DOM to do its rendering. This is normally behind the scenes, but content injection is one place in the API that allows you to use it. A function may return a virtual DOM node:

```js
import { Calendar } from 'fullcalendar'
import { createElement } from 'fullcalendar/preact' // subpath of same package

const calendar = new Calendar(calendarEl, {
  eventContent: (info) => (
    createElement('i', null,
      info.event.extendedProps.isUrgent ?
        'urgent event' :
        'normal event'
    )
  )
})
```

## Custom Toolbar Markup

The [CalendarController](https://fullcalendar.io/docs/CalendarController.md) technique for writing your own toolbar markup is only available in the [React](https://fullcalendar.io/docs/react.md#custom-toolbar-markup), [Preact](https://fullcalendar.io/docs/preact.md#custom-toolbar-markup) and [Vue](https://fullcalendar.io/docs/vue.md#custom-toolbar-markup) connectors. In vanilla JS, arrange the built-in toolbar with [headerToolbar](https://fullcalendar.io/docs/headerToolbar.md) and [footerToolbar](https://fullcalendar.io/docs/footerToolbar.md), and define your own buttons with [buttons](https://fullcalendar.io/docs/buttons.md). For anything beyond that, write your own markup and call the [date navigation methods](https://fullcalendar.io/docs/date-navigation.md) from it.

## Custom Views

It's possible to make calendar views that have custom rendering logic. See [Custom Views via JS](https://fullcalendar.io/docs/custom-view-with-js.md).

## Calendar API

In vanilla JS you already hold the `Calendar` object, so there is nothing to unwrap. Call methods on it directly:

```js
calendar.next();
```

This calls the [next](https://fullcalendar.io/docs/Calendar-next.md) method, which moves the calendar forward one month/week/day. Methods are documented throughout these docs alongside the features they belong to:

- [Date navigation](https://fullcalendar.io/docs/date-navigation.md) - `prev`, `next`, `today`, `gotoDate`, `incrementDate`. The [initialDate](https://fullcalendar.io/docs/initialDate.md) option only sets the _initial_ date.
- [Views](https://fullcalendar.io/docs/view-api.md) - `changeView`, and the current [View object](https://fullcalendar.io/docs/view-object.md)
- [Events](https://fullcalendar.io/docs/event-model.md) - `getEvents`, `getEventById`, `addEvent`, and the [Event object's](https://fullcalendar.io/docs/event-object.md) own methods
- [Event sources](https://fullcalendar.io/docs/event-source.md) - `addEventSource`, `getEventSources`, `refetchEvents`
- [Resources](https://fullcalendar.io/docs/resource-data.md) - `getResources`, `addResource`, `refetchResources` (premium)
- [Date selection](https://fullcalendar.io/docs/date-clicking-selecting.md) - `select`, `unselect`
- [Options](https://fullcalendar.io/docs/dynamic-options.md) - `getOption`, `setOption`
- [Handlers](https://fullcalendar.io/docs/handlers.md) - `on`, `off`

The calendar's own lifecycle is controlled with methods too:

- [Calendar::render](https://fullcalendar.io/docs/Calendar-render.md) displays the calendar. Nothing appears until it's called.
- [Calendar::destroy](https://fullcalendar.io/docs/Calendar-destroy.md) removes the calendar and restores the element.
- [Calendar::batchRendering](https://fullcalendar.io/docs/Calendar-batchRendering.md) groups multiple operations into a single rerender. The [rerenderDelay](https://fullcalendar.io/docs/rerenderDelay.md) option does so implicitly.

## FullCalendar Premium

How do you use [FullCalendar Premium's](https://fullcalendar.io/pricing) plugins in vanilla JS? They are no different than any other plugin. Whichever technique you use, make sure to include your [schedulerLicenseKey](https://fullcalendar.io/docs/schedulerLicenseKey.md).

### Premium via NPM

You'll need an additional package:

```sh
npm install --save fullcalendar-scheduler
```

Then, initialize your calendar:

```js
import { Calendar } from 'fullcalendar';
import themePlugin from 'fullcalendar/themes/monarch';
import resourceTimelinePlugin from 'fullcalendar-scheduler/resource-timeline';

// stylesheets
import 'fullcalendar/skeleton.css';
import 'fullcalendar/themes/monarch/theme.css';
import 'fullcalendar/themes/monarch/palettes/purple.css';

const calendarEl = document.getElementById('calendar');
const calendar = new Calendar(calendarEl, {
  schedulerLicenseKey: 'XXX',
  plugins: [themePlugin, resourceTimelinePlugin],
  initialView: 'resourceTimelineWeek',
  resources: [
    // your resource list
  ]
});
calendar.render();
```

Example repos:

- [View the **Vite** + **Scheduler** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/typescript-scheduler)
- [View the **Rollup** + **Scheduler** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/rollup-scheduler)

### Premium via Script Tags

First, follow the [standard script tag instructions](#via-script-tags) above. Then obtain the premium `fullcalendar-scheduler` bundle in one of the following ways:

- **Download**: <a href='https://github.com/fullcalendar/fullcalendar-workspace/releases/download/v7.1.0/fullcalendar-scheduler-7.1.0.zip'>fullcalendar-scheduler-7.1.0.zip</a>
- **CDN:** [jsdelivr](https://www.jsdelivr.com/package/npm/fullcalendar-scheduler?version=7.1.0)
- **NPM:** `npm install fullcalendar-scheduler`

Then, write the following initialization code:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />

    <!-- STANDARD + SCHEDULER JS -->
    <script src='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/all/global.js'></script>
    <script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@7.1.0/all/global.js'></script>

    <!-- THEME JS -->
    <script src='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/global.js'></script>

    <!-- STYLESHEETS -->
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/skeleton.css' rel='stylesheet' />
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/theme.css' rel='stylesheet' />
    <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.1.0/themes/monarch/palettes/purple.css' rel='stylesheet' />

    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const calendarEl = document.getElementById("calendar");
        const calendar = new FullCalendar.Calendar(calendarEl, {
          schedulerLicenseKey: "XXX",
          initialView: "resourceTimelineWeek",
        });
        calendar.render();
      });
    </script>
  </head>
  <body>
    <div id="calendar"></div>
  </body>
</html>
```

[View a runnable example →](https://fullcalendar.io/docs/timeline-standard-view-demo)

The `fullcalendar-scheduler` bundle's `all/global(.min).js` file includes the following plugins:

- `adaptive` (for [printer-friendly rendering](https://fullcalendar.io/docs/print.md))
- `scrollgrid`
- `timeline` ([more info](https://fullcalendar.io/docs/timeline-view-no-resources.md))
- `resource-daygrid` ([more info](https://fullcalendar.io/docs/resource-daygrid-view.md))
- `resource-timegrid` ([more info](https://fullcalendar.io/docs/vertical-resource-view.md))
- `resource-timeline` ([more info](https://fullcalendar.io/docs/timeline-view.md))

## TypeScript

Nothing special is needed for TypeScript integration. Types are included in the `fullcalendar` and `fullcalendar-scheduler` packages, so the [NPM technique](#via-npm) works as-is once your build system compiles TypeScript. [Vite](https://vite.dev/) does this out of the box:

```ts
import { Calendar, CalendarOptions } from 'fullcalendar';
import themePlugin from 'fullcalendar/themes/monarch';
import dayGridPlugin from 'fullcalendar/daygrid';

const calendarEl: HTMLElement = document.getElementById('calendar')!;

const options: CalendarOptions = {
  plugins: [themePlugin, dayGridPlugin],
  initialView: 'dayGridMonth'
};

const calendar = new Calendar(calendarEl, options);
calendar.render();
```

- [View the **FullCalendar + TypeScript + Vite** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/typescript)
- [View the **FullCalendar Scheduler + TypeScript + Vite** example repo →](https://github.com/fullcalendar/fullcalendar-examples/tree/main/typescript-scheduler)

## Options & Handlers

- [View-Specific Options](https://fullcalendar.io/docs/view-specific-options.md) - You can specify options that apply only to specific calendar views.
- [Get/Set Options Dynamically](https://fullcalendar.io/docs/dynamic-options.md) - You can get/set calendar options after a calendar has already been initialized.
- [Handlers](https://fullcalendar.io/docs/handlers.md) - How to attach handlers to your calendar, which execute when other things happen.
- [Content Injection](https://fullcalendar.io/docs/content-injection.md) - Custom content can be injected into FullCalendar's DOM in various places.

## Render & Lifecycle

- [Calendar::render](https://fullcalendar.io/docs/Calendar-render.md) - Initially render the calendar, or rerender it after initialization.
- [Calendar::destroy](https://fullcalendar.io/docs/Calendar-destroy.md) - Restores the container element to the state before FullCalendar was initialized.
- [Calendar::batchRendering](https://fullcalendar.io/docs/Calendar-batchRendering.md) - A way to group operations that cause rerenders.
- [rerenderDelay](https://fullcalendar.io/docs/rerenderDelay.md) - The amount of milliseconds to wait before rerendering anything on a calendar.
