---
title: V7 JavaScript API Migration Guide
---

<div class='sidebar-layout'>
<div class='sidebar-layout__sidebar sidebar-layout__sidebar--bordered' markdown='1'>

<a href="#" onclick={(e) => { e.preventDefault(); window.scrollTo({top:0,behavior:'smooth'}) }}>Back to top</a>

## Table of Contents

- [Before You Begin](#before-you-begin)
- Connectors
  - [React](#react)
  - [Vue](#vue)
  - [Angular](#angular)
  - [Vanilla JS](#vanilla-js)
  - [Web Component](#web-component)
- [Toolbar](#toolbar)
- [Events (General)](#events-general)
- [Resources (General)](#resources-general)
- Calendar Views
  - [General](#calendar-views-general)
  - [DayGrid View](#daygrid-view)
  - [Resource DayGrid View](#resource-daygrid-view)
  - [TimeGrid View](#timegrid-view)
  - [Resource TimeGrid View](#resource-timegrid-view)
  - [List View](#list-view)
  - [MultiMonth View](#multimonth-view)
  - [Resource Timeline View](#resource-timeline-view)
  - [Custom Views](#custom-views)
- Plugins
  - [Luxon Plugin](#luxon-plugin)
  - [Moment Plugins](#moment-plugins)
  - [Custom Plugins](#custom-plugins)
- [Custom Theme Class](#custom-theme-class)
- [Locales](#locales)
- [Interactions](#interactions)
- [Misc](#misc)


</div>
<div class='sidebar-layout__main changelog' markdown='1'>

This document will help you migrate from v6 of the API to v7. It centers around JS settings that must be changed. If you've written custom CSS, see the [CSS migration guide](css-migration) instead.


## Before You Begin

### Components in Examples

Many examples show React component pseudo-code with the `<FullCalendar>` element. Please adapt these examples to [Vue](vue) or [Angular](angular), minding the way FullCalendar treats components and component props for each library.

### clsx

Many migrations in this document show the merging of multiple class-names with the [clsx utility](https://www.npmjs.com/package/clsx). It's especially important for conditional class-names. Installation:

```bash
npm install --save clsx
```

In the JS:

```js
import clsx from 'clsx'

const bool = true
clsx('you', 'can', 'merge', bool && 'okay?') // "you can merge okay?"
```


## React

### React Standard Packages

**Breaking:** dependency changes:

- `@fullcalendar/core` removed as a peer dependency
- `@fullcalendar/daygrid` and many other packages removed and moved to `@fullcalendar/react/*` entrypoints (see `package.json` below)
- `temporal-polyfill` added as a peer dependency

1) So, your `package.json` should be modified:

```diff
{
  "dependencies": {
-   "@fullcalendar/react": "^6.1.20",
+   "@fullcalendar/react": "{latestReleases.v7}",
-   "@fullcalendar/core": "^6.1.20",
-   "@fullcalendar/interaction": "^6.1.20",
-   "@fullcalendar/daygrid": "^6.1.20",
-   "@fullcalendar/timegrid": "^6.1.20",
-   "@fullcalendar/list": "^6.1.20",
-   "@fullcalendar/multimonth": "^6.1.20",
  }
}
```

2) Then install `temporal-polyfill`:

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

3) In your JS, change your imports:

```diff
  import interactionPlugin
-   from '@fullcalendar/interaction'
+   from '@fullcalendar/react/interaction'
  import dayGridPlugin
-   from '@fullcalendar/daygrid'
+   from '@fullcalendar/react/daygrid'
  import timeGridPlugin
-   from '@fullcalendar/timegrid'
+   from '@fullcalendar/react/timegrid'
  import listPlugin
-   from '@fullcalendar/list'
+   from '@fullcalendar/react/list'
  import multiMonthPlugin
-   from '@fullcalendar/multimonth'
+   from '@fullcalendar/react/multimonth'
```

### React Scheduler Packages

**Breaking:** additional dependency changes for premium (aka "scheduler"):

1) Remove the following packages from your `package.json`:

```diff
{
  "dependencies": {
    "@fullcalendar/react": "{latestReleases.v7}",
    ...
-   "@fullcalendar/resource": "^6.1.20",
-   "@fullcalendar/adaptive": "^6.1.20",
-   "@fullcalendar/scrollgrid": "^6.1.20",
-   "@fullcalendar/timeline": "^6.1.20",
-   "@fullcalendar/resource-timeline": "^6.1.20",
-   "@fullcalendar/resource-timegrid": "^6.1.20"
  }
}
```

2) Install the `@fullcalendar/react-scheduler` package:

```bash
npm install --save @fullcalendar/react-scheduler@beta
```

3) In your JS, change your imports:

```diff
  import resourcePlugin
-   from '@fullcalendar/resource'
+   from '@fullcalendar/react-scheduler'
  import adaptivePlugin
-   from '@fullcalendar/adaptive'
+   from '@fullcalendar/react-scheduler/adaptive'
  import scrollGridPlugin
-   from '@fullcalendar/scrollgrid'
+   from '@fullcalendar/react-scheduler/scrollgrid'
  import timelinePlugin
-   from '@fullcalendar/timeline'
+   from '@fullcalendar/react-scheduler/timeline'
  import resourceTimelinePlugin
-   from '@fullcalendar/resource-timeline'
+   from '@fullcalendar/react-scheduler/resource-timeline'
  import resourceTimeGridPlugin
-   from '@fullcalendar/resource-timegrid'
+   from '@fullcalendar/react-scheduler/resource-timegrid'
```

**Breaking**: FullCalendar no longer bundles its own CSS. Furthermore, the calendar "theme" has been pulled out from the core, as a plugin. Thus, you must import your own stylesheets. This depends on what theme you choose (see [themes.fullcalendar.io](https://themes.fullcalendar.io)):

```diff
  import FullCalendar from '@fullcalendar/react'
+ import themePlugin from '@fullcalendar/react/themes/classic'
  // import other plugins...
  
  // stylesheets
+ import '@fullcalendar/react/skeleton.css' // ALWAYS NEED SKELETON
+ import '@fullcalendar/react/themes/classic/theme.css' // YOUR THEME
+ import '@fullcalendar/react/themes/classic/palette.css' // YOUR THEME'S PALETTE
  
  <FullCalendar
    plugins={[
+     themePlugin,
      // other plugins here...
    ]}
  />
```

### Remix

**Behavior:** The [Remix framework](https://remix.run/) previously required a hack to explicitly define the DOM-placement for styles, but this is no longer needed. In `app/root.tsx`:

```diff
  function Document({
    children,
    title,
  }: {
    children: React.ReactNode;
    title?: string;
  }) {
    return (
      <html lang="en">
        <head>
          {title ? <title>{title}</title> : null}
          <Meta />
-         <style data-fullcalendar />
          <Links />
        </head>
        <body>
          {children}
          <ScrollRestoration />
          <Scripts />
          <LiveReload />
        </body>
      </html>
    );
  }
```


## Vue

### Vue Standard Packages

**Breaking:** dependency changes:

- `@fullcalendar/core` removed as a peer dependency
- `@fullcalendar/daygrid` and many other packages removed and moved to `@fullcalendar/vue3/*` entrypoints (see `package.json` below)
- `temporal-polyfill` added as a peer dependency

1) So, your `package.json` should be modified:

```diff
{
  "dependencies": {
-   "@fullcalendar/vue3": "^6.1.20",
+   "@fullcalendar/vue3": "{latestReleases.v7}",
-   "@fullcalendar/core": "^6.1.20",
-   "@fullcalendar/interaction": "^6.1.20",
-   "@fullcalendar/daygrid": "^6.1.20",
-   "@fullcalendar/timegrid": "^6.1.20",
-   "@fullcalendar/list": "^6.1.20",
-   "@fullcalendar/multimonth": "^6.1.20"
  }
}
```

2) Then install `temporal-polyfill`:

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

3) In your JS, change your imports:

```diff
  import interactionPlugin
-   from '@fullcalendar/interaction'
+   from '@fullcalendar/vue3/interaction'
  import dayGridPlugin
-   from '@fullcalendar/daygrid'
+   from '@fullcalendar/vue3/daygrid'
  import timeGridPlugin
-   from '@fullcalendar/timegrid'
+   from '@fullcalendar/vue3/timegrid'
  import listPlugin
-   from '@fullcalendar/list'
+   from '@fullcalendar/vue3/list'
  import multiMonthPlugin
-   from '@fullcalendar/multimonth'
+   from '@fullcalendar/vue3/multimonth'
```

### Vue Scheduler Packages

**Breaking:** additional dependency changes for premium (aka "scheduler"):

1) Remove the following packages from your `package.json`:

```diff
{
  "dependencies": {
    "@fullcalendar/vue3": "{latestReleases.v7}",
    ...
-   "@fullcalendar/resource": "^6.1.20",
-   "@fullcalendar/adaptive": "^6.1.20",
-   "@fullcalendar/scrollgrid": "^6.1.20",
-   "@fullcalendar/timeline": "^6.1.20",
-   "@fullcalendar/resource-timeline": "^6.1.20",
-   "@fullcalendar/resource-timegrid": "^6.1.20"
  }
}
```

2) Install the `@fullcalendar/vue3-scheduler` package:

```bash
npm install --save @fullcalendar/vue3-scheduler@beta
```

3) In your JS, change your imports:

```diff
  import resourcePlugin
-   from '@fullcalendar/resource'
+   from '@fullcalendar/vue3-scheduler'
  import adaptivePlugin
-   from '@fullcalendar/adaptive'
+   from '@fullcalendar/vue3-scheduler/adaptive'
  import scrollGridPlugin
-   from '@fullcalendar/scrollgrid'
+   from '@fullcalendar/vue3-scheduler/scrollgrid'
  import timelinePlugin
-   from '@fullcalendar/timeline'
+   from '@fullcalendar/vue3-scheduler/timeline'
  import resourceTimelinePlugin
-   from '@fullcalendar/resource-timeline'
+   from '@fullcalendar/vue3-scheduler/resource-timeline'
  import resourceTimeGridPlugin
-   from '@fullcalendar/resource-timegrid'
+   from '@fullcalendar/vue3-scheduler/resource-timegrid'
```

**Breaking**: FullCalendar no longer bundles its own CSS. Furthermore, the calendar "theme" has been pulled out from the core, as a plugin. Thus, you must import your own stylesheets. This depends on what theme you choose (see [themes.fullcalendar.io](https://themes.fullcalendar.io)):

```diff
  <script setup>
    import FullCalendar from '@fullcalendar/vue3'
+   import themePlugin from '@fullcalendar/vue3/themes/classic'
    // import other plugins...
  
    // stylesheets
+   import '@fullcalendar/vue3/skeleton.css' // ALWAYS NEED SKELETON
+   import '@fullcalendar/vue3/themes/classic/theme.css' // YOUR THEME
+   import '@fullcalendar/vue3/themes/classic/palette.css' // YOUR THEME'S PALETTE
  </script>
  
  <template>
    <FullCalendar
      :options="{
        plugins: [
+         themePlugin,
          // other plugins here...
        ],
      }"
    />
  </template>
```


## Angular

### Angular Standard Packages

**Breaking:** dependency changes:

- The `@fullcalendar/core` peer dependency has been renamed `fullcalendar`
- `@fullcalendar/daygrid` and many other packages removed and moved to `@fullcalendar/angular/*` entrypoints (see `package.json` below)
- `temporal-polyfill` added as a peer dependency

1) So, your `package.json` should be modified:

```diff
{
  "dependencies": {
-   "@fullcalendar/angular": "^6.1.20",
+   "@fullcalendar/angular": "{latestReleases.v7}",
-   "@fullcalendar/core": "^6.1.20",
-   "@fullcalendar/interaction": "^6.1.20",
-   "@fullcalendar/daygrid": "^6.1.20",
-   "@fullcalendar/timegrid": "^6.1.20",
-   "@fullcalendar/list": "^6.1.20",
-   "@fullcalendar/multimonth": "^6.1.20"
  }
}
```

2) Then install the two additional peer dependencies:

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

3) In your JS, change your imports:

```diff
  import interactionPlugin
-   from '@fullcalendar/interaction'
+   from '@fullcalendar/angular/interaction'
  import dayGridPlugin
-   from '@fullcalendar/daygrid'
+   from '@fullcalendar/angular/daygrid'
  import timeGridPlugin
-   from '@fullcalendar/timegrid'
+   from '@fullcalendar/angular/timegrid'
  import listPlugin
-   from '@fullcalendar/list'
+   from '@fullcalendar/angular/list'
  import multiMonthPlugin
-   from '@fullcalendar/multimonth'
+   from '@fullcalendar/angular/multimonth'
```

### Angular Scheduler Packages

**Breaking:** additional dependency changes for premium (aka "scheduler"):

1) Remove the following packages from your `package.json`:

```diff
{
  "dependencies": {
    "@fullcalendar/angular": "{latestReleases.v7}",
    "fullcalendar": "{latestReleases.v7}",
    ...
-   "@fullcalendar/resource": "^6.1.20",
-   "@fullcalendar/adaptive": "^6.1.20",
-   "@fullcalendar/scrollgrid": "^6.1.20",
-   "@fullcalendar/timeline": "^6.1.20",
-   "@fullcalendar/resource-timeline": "^6.1.20",
-   "@fullcalendar/resource-timegrid": "^6.1.20"
  }
}
```

2) Install the `@fullcalendar/angular-scheduler` package:

```bash
npm install --save @fullcalendar/angular-scheduler@beta
```

3) In your JS, change your imports:

```diff
  import resourcePlugin
-   from '@fullcalendar/resource'
+   from '@fullcalendar/angular-scheduler'
  import adaptivePlugin
-   from '@fullcalendar/adaptive'
+   from '@fullcalendar/angular-scheduler/adaptive'
  import scrollGridPlugin
-   from '@fullcalendar/scrollgrid'
+   from '@fullcalendar/angular-scheduler/scrollgrid'
  import timelinePlugin
-   from '@fullcalendar/timeline'
+   from '@fullcalendar/angular-scheduler/timeline'
  import resourceTimelinePlugin
-   from '@fullcalendar/resource-timeline'
+   from '@fullcalendar/angular-scheduler/resource-timeline'
  import resourceTimeGridPlugin
-   from '@fullcalendar/resource-timegrid'
+   from '@fullcalendar/angular-scheduler/resource-timegrid'
```

**Breaking**: FullCalendar no longer bundles its own CSS. Furthermore, the calendar "theme" has been pulled out from the core, as a plugin. Thus, you must import your own stylesheets. This depends on what theme you choose (see [themes.fullcalendar.io](https://themes.fullcalendar.io)):

```diff
  import { FullCalendarModule } from '@fullcalendar/angular';
  import { FullCalendarModule, CalendarOptions } from '@fullcalendar/angular';
+ import themePlugin from '@fullcalendar/angular/themes/classic'; // YOUR THEME
  // import other plugins...

  export class App {
    calendarOptions = signal<CalendarOptions>({
      plugins: [
+       themePlugin,
        // other plugins here...
      ],
    })
```

Add the following stylesheets to your `angular.json`:

```diff
  {
    "projects": {        // ...
      "my-project": {    // ...
        "architect": {   // ...
          "build": {     // ...
            "options": { // ...
              "styles": [
+               "@fullcalendar/angular/skeleton.css", // ALWAYS NEED SKELETON
+               "@fullcalendar/angular/themes/classic/theme.css", // YOUR THEME
+               "@fullcalendar/angular/themes/classic/palette.css", // YOUR THEME'S PALETTE
                "src/styles.css"
              ]
```


## Vanilla JS

### Vanilla JS Standard Packages

**Breaking:** dependency changes:

- Vanilla JS package `@fullcalendar/core` renamed to `fullcalendar`
- `@fullcalendar/daygrid` and many other packages removed and moved to `fullcalendar/*` entrypoints (see `package.json` below)
- `temporal-polyfill` added as a peer dependency

1) So, your `package.json` should be modified:

```diff
{
  "dependencies": {
-   "@fullcalendar/core": "^6.1.20",
-   "@fullcalendar/interaction": "^6.1.20",
-   "@fullcalendar/daygrid": "^6.1.20",
-   "@fullcalendar/timegrid": "^6.1.20",
-   "@fullcalendar/list": "^6.1.20",
-   "@fullcalendar/multimonth": "^6.1.20"
  }
}
```

2) Then install the renamed vanilla JS connector and `temporal-polyfill`:

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

3) In your JS, change your imports:

```diff
  import interactionPlugin
-   from '@fullcalendar/interaction'
+   from 'fullcalendar/interaction' // no @ at the beginning!
  import dayGridPlugin
-   from '@fullcalendar/daygrid'
+   from 'fullcalendar/daygrid' // no @ at the beginning!
  import timeGridPlugin
-   from '@fullcalendar/timegrid'
+   from 'fullcalendar/timegrid' // no @ at the beginning!
  import listPlugin
-   from '@fullcalendar/list'
+   from 'fullcalendar/list' // no @ at the beginning!
  import multiMonthPlugin
-   from '@fullcalendar/multimonth'
+   from 'fullcalendar/multimonth' // no @ at the beginning!
```

### Vanilla JS Scheduler Packages

**Breaking:** additional dependency changes for premium (aka "scheduler"):

1) Remove the following packages from your `package.json`:

```diff
{
  "dependencies": {
    "fullcalendar": "{latestReleases.v7}",
    ...
-   "@fullcalendar/resource": "^6.1.20",
-   "@fullcalendar/adaptive": "^6.1.20",
-   "@fullcalendar/scrollgrid": "^6.1.20",
-   "@fullcalendar/timeline": "^6.1.20",
-   "@fullcalendar/resource-timeline": "^6.1.20",
-   "@fullcalendar/resource-timegrid": "^6.1.20"
  }
}
```

2) Install the `fullcalendar-scheduler` package:

```bash
npm install --save fullcalendar-scheduler@beta
```

3) In your JS, change your imports:

```diff
  import resourcePlugin
-   from '@fullcalendar/resource'
+   from 'fullcalendar-scheduler'
  import adaptivePlugin
-   from '@fullcalendar/adaptive'
+   from 'fullcalendar-scheduler/adaptive'
  import scrollGridPlugin
-   from '@fullcalendar/scrollgrid'
+   from 'fullcalendar-scheduler/scrollgrid'
  import timelinePlugin
-   from '@fullcalendar/timeline'
+   from 'fullcalendar-scheduler/timeline'
  import resourceTimelinePlugin
-   from '@fullcalendar/resource-timeline'
+   from 'fullcalendar-scheduler/resource-timeline'
  import resourceTimeGridPlugin
-   from '@fullcalendar/resource-timegrid'
+   from 'fullcalendar-scheduler/resource-timegrid'
```

**Breaking**: FullCalendar no longer bundles its own CSS. Furthermore, the calendar "theme" has been pulled out from the core, as a plugin. Thus, you must import your own stylesheets. This depends on what theme you choose (see [themes.fullcalendar.io](https://themes.fullcalendar.io)):

```diff
- import { Calendar } from '@fullcalendar/core'
+ import { Calendar } from 'fullcalendar'
+ import themePlugin from 'fullcalendar/themes/classic' // YOUR THEME
  // import other plugins...
  
  // stylesheets
+ import 'fullcalendar/skeleton.css'
+ import 'fullcalendar/themes/classic/theme.css' // YOUR THEME
+ import 'fullcalendar/themes/classic/palette.css' // YOUR THEME'S PALETTE
  
  const calendarEl = document.getElementById('calendar')
  const calendar = new Calendar(calendarEl, {
    plugins: [
+     themePlugin,
      // other plugins here...
    ],
  })
  calendar.render()
```

### Global Script Tags

**Breaking:** `index.global.js` renamed to `all.global.js` (which contains "all" standard plugins), and additional scripts/stylesheets must be added for the theme:

```diff
  <!-- STANDARD JS -->
- <script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.20/index.global.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/all.global.js'></script>

  <!-- THEME JS -->
+ <script src='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/themes/classic/global.js'></script>
  
  <!-- STYLESHEETS -->
+ <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/skeleton.css' rel='stylesheet' />
+ <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/themes/classic/theme.css' rel='stylesheet' />
+ <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/themes/classic/palette.css' rel='stylesheet' />
```

**Breaking:** Similar breaking changes for scheduler, but with an additional one: scheduler's JS file *no longer contains the standard plugins*, so both standard and premium `all.global.js` files must be included:

```diff
  <!-- STANDARD + SCHEDULER JS -->
- <script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@6.1.20/index.global.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/all.global.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/fullcalendar-scheduler@7.0.0-beta.7/all.global.js'></script>

  <!-- THEME JS -->
+ <script src='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/themes/classic/global.js'></script>
  
  <!-- STYLESHEETS -->
+ <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/skeleton.css' rel='stylesheet' />
+ <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/themes/classic/theme.css' rel='stylesheet' />
+ <link href='https://cdn.jsdelivr.net/npm/fullcalendar@7.0.0-beta.7/themes/classic/palette.css' rel='stylesheet' />
```


## Web Component

### Web Component Standard Packages

**Breaking:** dependency changes:

- `@fullcalendar/core` removed as a peer dependency
- `@fullcalendar/daygrid` and many other packages removed (see `package.json` below)
- `temporal-polyfill` added as a peer dependency

1) So, your `package.json` should be modified:

```diff
{
  "dependencies": {
-   "@fullcalendar/web-component": "^6.1.20",
+   "@fullcalendar/web-component": "{latestReleases.v7}",
-   "@fullcalendar/core": "^6.1.20",
-   "@fullcalendar/interaction": "^6.1.20",
-   "@fullcalendar/daygrid": "^6.1.20",
-   "@fullcalendar/timegrid": "^6.1.20",
-   "@fullcalendar/list": "^6.1.20",
-   "@fullcalendar/multimonth": "^6.1.20"
  }
}
```

2) Then install the upgraded web-component connector and `temporal-polyfill`:

```bash
npm install --save @fullcalendar/web-component@beta temporal-polyfill
```

3) In your JS, change your imports:

```diff
  import interactionPlugin
-   from '@fullcalendar/interaction'
+   from '@fullcalendar/web-component/interaction'
  import dayGridPlugin
-   from '@fullcalendar/daygrid'
+   from '@fullcalendar/web-component/daygrid'
  import timeGridPlugin
-   from '@fullcalendar/timegrid'
+   from '@fullcalendar/web-component/timegrid'
  import listPlugin
-   from '@fullcalendar/list'
+   from '@fullcalendar/web-component/list'
  import multiMonthPlugin
-   from '@fullcalendar/multimonth'
+   from '@fullcalendar/web-component/multimonth'
```

### Web Component Scheduler Packages

**Breaking:** additional dependency changes for premium (aka "scheduler"):

1) Remove the following packages from your `package.json`:

```diff
{
  "dependencies": {
    "@fullcalendar/web-component": "{latestReleases.v7}",
    ...
-   "@fullcalendar/resource": "^6.1.20",
-   "@fullcalendar/adaptive": "^6.1.20",
-   "@fullcalendar/scrollgrid": "^6.1.20",
-   "@fullcalendar/timeline": "^6.1.20",
-   "@fullcalendar/resource-timeline": "^6.1.20",
-   "@fullcalendar/resource-timegrid": "^6.1.20"
  }
}
```

2) Install the `@fullcalendar/web-component-scheduler` package:

```bash
npm install --save @fullcalendar/web-component-scheduler@beta
```

3) In your JS, change your imports:

```diff
  import resourcePlugin
-   from '@fullcalendar/resource'
+   from '@fullcalendar/web-component-scheduler'
  import adaptivePlugin
-   from '@fullcalendar/adaptive'
+   from '@fullcalendar/web-component-scheduler/adaptive'
  import scrollGridPlugin
-   from '@fullcalendar/scrollgrid'
+   from '@fullcalendar/web-component-scheduler/scrollgrid'
  import timelinePlugin
-   from '@fullcalendar/timeline'
+   from '@fullcalendar/web-component-scheduler/timeline'
  import resourceTimelinePlugin
-   from '@fullcalendar/resource-timeline'
+   from '@fullcalendar/web-component-scheduler/resource-timeline'
  import resourceTimeGridPlugin
-   from '@fullcalendar/resource-timegrid'
+   from '@fullcalendar/web-component-scheduler/resource-timegrid'
```

**Breaking**: FullCalendar no longer bundles its own CSS. Furthermore, the calendar "theme" has been pulled out from the core, as a plugin. Thus, you must import your own stylesheets. This depends on what theme you choose (see [themes.fullcalendar.io](https://themes.fullcalendar.io)):

```diff
  import '@fullcalendar/web-component/global'
+ import themePlugin from '@fullcalendar/web-component/themes/classic'
  // import other plugins...
  
  // stylesheets
+ import '@fullcalendar/web-component/skeleton.styles'
+ import '@fullcalendar/web-component/themes/classic/theme.styles'
+ import '@fullcalendar/web-component/themes/classic/palette.css'
  
  const fullCalendarElement = document.querySelector('full-calendar')
  fullCalendarElement.options = {
    plugins: [
+     themePlugin,
      // other plugins here...
    ]
  }
```

```html
<!-- HTML -->
<full-calendar></full-calendar>
```

### Global Script Tags

**Breaking:** `index.global.js` renamed to `all.global.js` (which contains "all" standard plugins), and additional scripts/stylesheets must be added for the theme:

```diff
  <!-- STANDARD JS -->
- <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/core@6.1.20/index.global.js'></script>
- <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@6.1.20/index.global.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/all.global.js'></script>
  
  <!-- THEME JS -->
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/themes/classic/global.js'></script>

  <!-- STYLESHEETS -->
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/skeleton.styles.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/themes/classic/theme.styles.js'></script>
+ <link href='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/themes/classic/palette.css' rel='stylesheet' />
```

Please note, `skeleton.styles.js` and `theme.styles.js` are `<script>` tags, and yes they do inject CSS. This is required for the web component's shadow DOM.

**Breaking:** Similar breaking changes for scheduler, but with an additional one: scheduler's JS file *no longer contains the standard plugins*, so both standard and premium `all.global.js` files must be included:

```diff
  <!-- STANDARD + SCHEDULER JS -->
- <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component-scheduler@6.1.20/index.global.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/all.global.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component-scheduler@7.0.0-beta.7/all.global.js'></script>

  <!-- THEME JS -->
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/themes/classic/global.js'></script>
  
  <!-- STYLESHEETS -->
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/skeleton.styles.js'></script>
+ <script src='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/themes/classic/theme.styles.js'></script>
+ <link href='https://cdn.jsdelivr.net/npm/@fullcalendar/web-component@7.0.0-beta.7/themes/classic/palette.css' rel='stylesheet' />
```


## Toolbar

**Breaking:** `headerToolbar` is now disabled by default. It no longer shows the title and buttons by default. If you have no `headerToolbar` specified and want to restore:

```diff
  <FullCalendar
+   headerToolbar={{
+     start: 'title',
+     end: 'today prev,next',
+   }}
  />
```

**Breaking:** `customButtons` renamed to `buttons` (used for custom *and* standard buttons). `buttonText` removed in favor of `buttons[].text`:

```diff
  <FullCalendar
-   buttonText={{
-     hello: 'Hello'
-   }}
-   customButtons={{
+   buttons={{
      hello: {
+       text: 'Hello'
        click() {
          alert('hello!')
        }
      }
    }}
  />
```

**Breaking:** `buttonIcons` removed. Attach a custom class-name and associated glyphicon with `buttons[].iconClass`:

```diff
  <FullCalendar
-   buttonIcons={{
-     next: 'my-button-icon'
-   }}
+   buttons={{
+     next: {
+       iconClass: 'my-button-icon'
+     }
+   }}
  />
```

However, SVGs are now preferred over glyphicons, and can be attached with `buttons[].iconContent` ([more info](buttons)).

**Type change:** each entry of the `buttons` map: 

```diff
- import { type CustomButtonInput } from '@fullcalendar/core'
+ import { type ButtonInput } from '@fullcalendar/react' // or angular/vue3/etc
```


## Events (General)

The changes here apply to events in all views.

**Breaking:** All class-name properties have been renamed and only accept simple string:

```diff
  <FullCalendar
    // calendar-wide
-   eventClassNames={['my-event', 'my-util']}
+   eventClass='my-event my-util'
  
    // event-specific
    events={[
      title: 'My Event',
      start: '2026-02-02',
-     classNames: ['my-event', 'my-util']
+     className: 'my-event my-util'
    ]}
  
    // by event-source
    eventSources={[
      {
        url: '/my-feed.php',
-       classNames: ['my-event', 'my-util']
+       className: 'my-event my-util'
      }
    ]}
  
    // by resource
    resources={[
      {
        id: 'a',
-       eventClassNames: ['my-event', 'my-util']
+       eventClass: 'my-event my-util'
      }
    ]}
  />

  // via Event API
  const event = calendarApi.getEventById('a')
- event.classNames // array of strings
+ event.className // string

  // via Resource API
  const resource = calendarApi.getResourceById('b')
- resource.eventClassNames // array of string
+ resource.eventClass // string
```

**Breaking:** `eventClass` (formerly `eventClassNames`), `eventContent`, `eventDidMount`, `eventWillUnmount` *no longer apply to background events*. If you intentially styled ALL types of events using `eventClass`, and want to restore that behavior in v7, do this:

```diff
  <FullCalendar
    eventClass='all-events'
+   backgroundEventClass='all-events'
  />
```

**Breaking:** In v6, event color customization would affect literal parts of the element (border, background, text). In v7, the properties are more semantic and the individual theme can decide what to do with them.

```diff
  <FullCalendar
    // calendar-wide
-   eventBackgroundColor='red' // these can no longer be set separately...
-   eventBorderColor='red'     // ...use eventColor instead...
    eventColor='red'
-   eventTextColor='white'
+   eventContrastColor='white'

    // event-specific
    events={[
      {
        title: 'My Event',
        start: '2026-02-02',
-       backgroundColor: 'red',
-       borderColor: 'red',
        color: 'red',
-       textColor: 'white',
+       contrastColor: 'white',
      }
    ]}

    // by event-source
    eventSources={[
      {
        url: '/my-feed.php',
-       backgroundColor: 'red',
-       borderColor: 'red',
        color: 'red',
-       textColor: 'white',
+       contrastColor: 'white',
      }
    ]}

    // by resource
    resources={[
      {
        id: 'a',
-       eventBackgroundColor: 'red',
-       eventBorderColor: 'red',
        eventColor: 'red',
-       eventTextColor: 'white',
+       eventContrastColor: 'white',
      }
    ]}
  />

  // via Event API
  const event = calendarApi.getEventById('a')
- event.backgroundColor
- event.borderColor
+ event.color
- event.textColor
+ event.contrastColor

  // via Resource API
  const resource = calendarApi.getResourceById('b')
- resource.eventBackgroundColor
- resource.eventBorderColor
+ resource.eventColor
- resource.eventTextColor
+ resource.eventContrastColor
```

If you've customized the classic theme to have events with border colors that are DIFFERENT than their background colors, and want to maintain that look in v7, you'll need to fork the theme.

**Type change:** The parameter passed to `eventClass` (formerly `eventClassNames`), `eventContent`, `eventDidMount`, and `eventWillUnmount` has changed:

```diff
- import { type EventContentArg } from '@fullcalendar/core'
+ import { type EventDisplayInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

**Breaking:** Event API's `.toPlainObject()` and `.toJSON()` methods have modified signatures and return types:

```diff
  const res = event.toPlainObject({ // or .toJSON()
-   collapseColor: true
  })
  
- res.classNames // array of strings
+ res.className // string
  
- res.backgroundColor
- res.borderColor
+ res.color
  
- res.textColor
+ res.contrastColor
```


## Resources (General)

**Breaking:** The generic `resourceLabel*` settings have been replaced with more UI-specific settings. Class-name settings now use simple strings instead of arrays:

- [Resource Day Header](#resource-day-header) for **Resource DayGrid** and **Resource TimeGrid** views
- [Resource Cell](#resource-cell) for **Resource Timeline** view

**Breaking:** Resource API's `.toPlainObject()` and `.toJSON()` methods have modified signatures and return types:

```diff
  const res = event.toPlainObject({ // or .toJSON()
-   collapseColor: true
  })
  
- res.eventClassNames // array of strings
+ res.eventClass // string
  
- res.eventBackgroundColor
- res.eventBorderColor
+ res.eventColor
  
- res.eventTextColor
+ res.eventContrastColor
```


## Calendar Views (General)

The follow changes apply to all calendar views.


### Root Element

**Breaking:** `viewClassNames` renamed and accepts only a simple string:

```diff
  <FullCalendar
-   viewClassNames={['my-view', 'my-util']}
+   viewClass='my-view my-util'
  />
```

**Types change:** The parameter given to all `view*` callbacks has changed:

```diff
- import { type ViewContentArg } from '@fullcalendar/core'
+ import { type ViewDisplayInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Nav-Links

**Breaking:** `navLinkClassNames` rename and accepts only a simple string:

```diff
  <FullCalendar
-   navLinkClassNames={['my-nav-link', 'my-util']}
+   navLinkClass='my-nav-link my-util'
  />
```

### More-Links

**Breaking:** `moreLinkClassNames` rename and accepts only a simple string:

```diff
  <FullCalendar
-   moreLinkClassNames={['my-more-link', 'my-util']}
+   moreLinkClass='my-more-link my-util'
  />
```

**Types change:** The parameter given to all `moreLink*` callbacks has changed:

```diff
- import { type MoreLinkContentArg } from '@fullcalendar/core'
+ import { type MoreLinkInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Week Number

**Breaking:** The generic `weekNumber*` settings have been replaced with more UI-specific settings. Class-name settings now use simple strings instead of arrays:

- [Inline Week Number](#inline-week-number) for **DayGrid** view
- [Week Number Header](#week-number-header) for **TimeGrid** view


### All-Day

**Breaking:** The generic `allDay*` settings have been replaced with more UI-specific settings. Class-name settings now use simple strings instead of arrays:

- [All-Day Header](#all-day-header) for **TimeGrid** view
- [All-Day Text on Events](#all-day-text-on-events) for **List** view


### Time Slot Header

Time slots are present in [TimeGrid View](#timegrid-view) and [Timeline View](#timegrid-view). The changes here apply to both views.

**Breaking**: slot "label" settings have been renamed to slot "header". Also, class-names must be simple strings:

```diff
<FullCalendar
- slotLabelClassNames={['my-slot', 'my-util']}
+ slotHeaderClass='my-slot my-util'

- slotLabelContent={(data) => <span>{data.text}</span>}
+ slotHeaderContent={(data) => <span>{data.text}</span>}

- slotLabelDidMount={(data) => { /* handler */ }}
+ slotHeaderDidMount={(data) => { /* handler */ }}

- slotLabelWillUnmount={(data) => { /* handler */ }}
+ slotHeaderWillUnmount={(data) => { /* handler */ }}
/>
```

**Type change:**  The parameter given to the callbacks has changed:

```diff
- import { type SlotLabelContentArg } from '@fullcalendar/core'
+ import { type SlotHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Time Slot Lane

Time slots are present in [TimeGrid View](#timegrid-view) and [Timeline View](#timegrid-view). The changes here apply to both views.

**Breaking:** the `slotLaneClassNames` setting renamed. Must be a simple string. `slotLaneContent` removed:

```diff
<FullCalendar
- slotLaneClassNames={['my-slot', 'my-util']}
+ slotLaneClass='my-slot my-util'

  // NO REPLACEMENT - lane inner-content can no longer be customized
- slotLaneContent={(data) => <span>...</span>}
/>
```

**Type change:** The parameter given to the `slotLane*` callbacks has changed:

```diff
- import { type SlotLaneContentArg } from '@fullcalendar/core'
+ import { type SlotLaneInfo } from '@fullcalendar/react' // or angular/vue3/etc
```


### Now-Indicator

A now-indicator is present in [TimeGrid View](#timegrid-view) and [Timeline View](#timegrid-view). The changes here apply to both views.

**Breaking:** in v6, the `nowIndicator*` settings represented BOTH the "arrow", which appeared in the time axis, and the "line", which appeared over a day. One would differentiate based on `data.isAxis` (now removed). In v7, there are distinct settings for each:

<div class='diff-code-2col' markdown='1'>
<div>

```diff
<FullCalendar
- nowIndicatorClassNames={(data) => (
-   data.isAxis
-     ? ['my-arrow', 'my-util']
-     : ['my-line', 'my-util']
- )}

- nowIndicatorContent={(data) => (
-   data.isAxis
-     ? <span>the axis content</span>
-     : <span>the line content</span>
- )}

- nowIndicatorDidMount={(data) => {
-   if (data.isAxis) {
-     // for axis
-   } else {
-     // for line
-   }
- }}

- nowIndicatorWillUnmount={(data) => {
-   if (data.isAxis) {
-     // for axis
-   } else {
-     // for line
-   }
- }}
/>
```

</div>
<div>

```diff
<FullCalendar

+  nowIndicatorHeaderClass='my-arrow my-util'
+  nowIndicatorLineClass='my-line my-util'


+ nowIndicatorHeaderContent={(data) => (
+   <span>the axis content</span>
+ )}
+ nowIndicatorLineContent={(data) => (
+   <span>the line content</span>
+ )}


+ nowIndicatorHeaderDidMount={(data) => {
+   // for axis
+ }}
+ nowIndicatorLineDidMount={(data) => {
+   // for line
+ }}


+ nowIndicatorHeaderWillUnmount={(data) => {
+   // for axis
+ }}
+ nowIndicatorLineWillUnmount={(data) => {
+   // for line
+ }}
/>
```

</div>
</div>

**Type changes:** The parameter given to the callbacks has also been split in two:

```diff
- import { NowIndicatorContentArg } from '@fullcalendar/core'

// for axis
+ import { NowIndicatorHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc

// for line
+ import { NowIndicatorLineInfo } from '@fullcalendar/react' // or angular/vue3/etc
```


## DayGrid View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)

### Day Header

**Breaking:** `dayHeaderClassNames` renamed and accepts only a simple string. Can no longer do horizontal alignment:

```diff
<FullCalendar
- dayHeaderClassNames={['my-header', 'my-util']}
+ dayHeaderClass='my-header my-util'
+ dayHeaderAlign='start' // horizontal alignment (in LTR, 'start' means left)
/>

/* in CSS */
.my-header {
- text-align: left; /* horizontal alignment */
}
```

The `dayHeader*` settings now apply to the popover header, which is usually desirable. See [Popover CSS migration docs](css-migration#popover).

<strong id='day-header-classic-bold'>Breaking (Classic Theme):</strong> The day-headers are no longer bold by default. To restore:

```diff
<FullCalendar
+ dayHeaderClass='my-day-header'
/>

/* CSS */
+ .my-day-header {
+   font-weight: bold;
+ }
```

**Types change:** The parameter given to all `dayHeader*` callbacks has changed:

```diff
- import { type DayHeaderContentArg } from '@fullcalendar/core'
+ import { type DayHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Day Cell

**Breaking:** `dayCellClassNames` renamed and accepts only a simple string. `dayCellContent` renamed:

```diff
<FullCalendar
- dayCellClassNames={['my-cell', 'my-util']}
+ dayCellClass='my-cell my-util'

- dayCellContent={(data) => (
+ dayCellTopContent={(data) => (
    <span>In place of day-number</span>
  )}
/>
```

The `dayCell*` settings now apply to the popover body, which is usually desirable. See [Popover CSS migration docs](css-migration#popover).

The `dayCell*` settings previously applied to [TimeGrid Day Lane](#day-lane), but no longer.

**Types change:** The parameter given to all `dayCell*` callbacks has changed:

```diff
- import { type DayCellContentArg } from '@fullcalendar/core'
+ import { type DayCellInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Inline Week Number

**Breaking:** Week numbers, as they render in a DayGrid, are now called "inline week numbers" because they rest within the cells, not as a separate column. Thus, they have been renamed. You can configure them as top-level settings, and class-name settings must be simple strings:

```diff
<FullCalendar
- views={{
-   dayGrid: {
-     weekNumberClassNames: ['my-week-number', 'my-util']
-     weekNumberContent: (data) => <span>{data.text}</span>
-     weekNumberDidMount: (data) => { /* handler */ }
-     weekNumberWillUnmount: (data) => { /* handler */ }
-   }
- }}
+ inlineWeekNumberClass='my-week-number my-util'
+ inlineWeekNumberContent={(data) => <span>{data.text}</span>}
+ inlineWeekNumberDidMount={(data) => { /* handler */ }}
+ inlineWeekNumberWillUnmount={(data) => { /* handler */ }}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type WeekNumberContentArg } from '@fullcalendar/core'
+ import { type InlineWeekNumberInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Events

<strong id='event-inner-and-dot'>Breaking:</strong> In v6, for list-item events in DayGrid view, the content generated <code>eventContent</code> would clear rendering of the colored dot. In v7, <code>eventContent</code> injects into the "inner" container (aka <code>eventInnerClass</code>) which does not wrap the dot (aka <code>eventBeforeClass</code>). To restore old behavior:

```diff
  <FullCalendar
    eventContent={(arg) => <span>Cool {arg.event.title}</span>}
    views={{
+     dayGrid: { listItemEventBeforeClass: 'display-none' },
+     timeGrid: { listItemEventBeforeClass: 'display-none' }
    }}
  />

/* in CSS */
+ .display-none { display: none }
```

## Resource DayGrid View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)
- [DayGrid View](#daygrid-view)

### Resource Day Header

**Breaking:** "Resource labels", as they render in DayGrid and TimeGrid, are now called "resource day headers". You can configure them as top-level settings, and class-name settings must be simple strings:

```diff
<FullCalendar
- views={{
-   resourceDayGrid: { // OR resourceTimeGrid
-     resourceLabelClassNames: ['my-header', 'my-util'],
-     resourceLabelContent: (data) => <span>{data.text}</span>,
-     resourceLabelDidMount: (data) => { /* handler */ },
-     resourceLabelWillUnmount: (data) => { /* handler */ },
-   }
- }}
+ resourceDayHeaderClass='my-header my-util'
+ resourceDayHeaderAlign='start' // horizontal alignment (in LTR, 'start' means left)
+ resourceDayHeaderContent={(data) => <span>{data.text}</span>}
+ resourceDayHeaderDidMount={(data) => { /* handler */ }}
+ resourceDayHeaderWillUnmount={(data) => { /* handler */ }}
/>

/* in CSS */
.my-header {
- text-align: left; /* horizontal alignment */
}
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type ResourceLabelContentArg } from '@fullcalendar/core'
+ import { type ResourceDayHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc
```


## TimeGrid View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)
- [DayGrid View](#daygrid-view) - for "day headers" and all-day "day cells"

### Week Number Header

**Breaking:** Week numbers, as they render in a TimeGrid, are now called "week number headers" because they're located in the header of the view, above the time axis. They have been renamed and can be configured as top-level settings, and class-name settings must be simple strings:

```diff
<FullCalendar
- views={{
-   timeGrid: {
-     weekNumberClassNames: ['my-week-header', 'my-util']
-     weekNumberContent: (data) => <span>{data.text}</span>
-     weekNumberDidMount: (data) => { /* handler */ }
-     weekNumberWillUnmount: (data) => { /* handler */ }
-   }
- }}
+ weekNumberHeaderClass='my-week-header my-util'
+ weekNumberHeaderContent={(data) => <span>{data.text}</span>}
+ weekNumberHeaderDidMount={(data) => { /* handler */ }}
+ weekNumberHeaderWillUnmount={(data) => { /* handler */ }}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type WeekNumberContentArg } from '@fullcalendar/core'
+ import { type WeekNumberHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### All-Day Header

**Breaking:** The cell labeling the all-day section has been renamed to "all day header". Class-name settings must be simple strings:

```diff
<FullCalendar
- views={{
-   timeGrid: {
-     allDayClassNames: ['my-all-day', 'my-util'],
-     allDayContent: (data) => <span>{data.text}</span>,
-     allDayDidMount: (data) => { /* handler */ },
-     allDayWillUnmount: (data) => { /* handler */ },
-   }
- }}
+ allDayHeaderClass='my-all-day my-util'
+ allDayHeaderContent={(data) => <span>{data.text}</span>}
+ allDayHeaderDidMount={(data) => { /* handler */ }}
+ allDayHeaderWillUnmount={(data) => { /* handler */ }}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type AllDayContentArg } from '@fullcalendar/core'
+ import { type AllDayHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Day Lane

**Breaking:** TimeGrid lane hooks are renamed from `dayCell*` to `dayLane*`, lane class-name settings must be simple strings, and lane content customization is no longer supported.

```diff
<FullCalendar
- dayCellClassNames={['my-lane', 'my-util']}
+ dayLaneClass='my-lane my-util'

  // NO REPLACEMENT - can't customize content in here anymore
- dayCellContent={(data) => <span>...</span>}

- dayCellDidMount={(data) => { /* handler */ }}
+ dayLaneDidMount={(data) => { /* handler */ }}

- dayCellWillUnmount={(data) => { /* handler */ }}
+ dayLaneWillUnmount={(data) => { /* handler */ }}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type DayCellContentArg } from '@fullcalendar/core'
+ import { type DayLaneInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

## Resource TimeGrid View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)
- [TimeGrid View](#timegrid-view)
- [DayGrid View](#daygrid-view) - for "day headers" and all-day "day cells"


## List View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)

### Day Header

**Breaking:** List-view day header hooks moved from `views.list.dayHeader*` to top-level `listDayHeader*` settings, and class-name must be a simple string.

```diff
<FullCalendar
- views={{
-   list: {
-     dayHeaderClassNames: ['my-header', 'my-util'],
-     dayHeaderContent: (data) => <span>{data.text}</span>,
-     dayHeaderDidMount: (data) => { /* handler */ },
-     dayHeaderWillUnmount: (data) => { /* handler */ },
-   }
- }}
+ listDayHeaderClass='my-header my-util'
+ listDayHeaderContent={(data) => <span>{data.text}</span>}
+ listDayHeaderDidMount={(data) => { /* handler */ }}
+ listDayHeaderWillUnmount={(data) => { /* handler */ }}
/>
```

Furthermore, whereas `dayHeaderContent` affected the entire day-header in list-view, [`listDayHeaderContent`](list-day-header-render-hooks) is called for each subpart of the header, one for [`listDayFormat`](listDayFormat) (`level:0`) and one for [`listDayAltFormat`](listDayAltFormat) (`level:1`):

```diff
<FullCalendar
- views={{
-   list: {
-     dayHeaderContent: (data) => (
-       <>
-         <strong>{data.text}</strong>
-         <em>{data.sideText</em>
-       </>
-     )
-   }
- }}
+ listDayHeaderContent={(data) => (
+   data.level === 0 ? (
+     <strong>{data.text}</strong>
+   ) : (
+     <em>{data.text}</em>
+   )
+ )}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type DayHeaderContentArg } from '@fullcalendar/core'

// for listDayHeaderClass, listDayHeaderDidMount, listDayHeaderWillUnmount
+ import { type ListDayHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc

// for listDayHeaderContent, listDayHeaderInnerClass (new)
+ import { type ListDayHeaderInnerInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### All-Day Text on Events

**Breaking:** In List view, all-day event text is no longer customized via generic `allDay*` hooks. Use list `event*` hooks instead.

```diff
<FullCalendar
- allDayClassNames={['my-all-day-text', 'my-util']}
- allDayContent={(data) => <span>{data.text}</span>}
- allDayDidMount={(data) => { /* handler */ }}
- allDayWillUnmount={(data) => { /* handler */ }}
+ views={{
+   list: {
+     eventTimeClass: 'my-all-day-text my-util',
+     eventContent: (data) => (
+       <>
+         <div className={data.timeClass}><i>{data.timeText}</i></div>
+         <div className={data.titleClass}>{data.titleText}</div>
+       </>
+     ),
+     eventDidMount: (data) => {
+       data.el.querySelector('.my-all-day-text')
+     },
+     eventWillUnmount: (data) => {
+       data.el.querySelector('.my-all-day-text')
+     }
+   }
+ }}
/>
```

### No-Events Screen

**Breaking:** `noEventsClassNames` has been renamed to `noEventsClass`, which accepts only a simple string.

```diff
<FullCalendar
- noEventsClassNames={['my-no-events', 'my-util']}
+ noEventsClass='my-no-events my-util'
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type NoEventsContentArg } from '@fullcalendar/core'
+ import { type NoEventsInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

## MultiMonth View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)
- [DayGrid View](#daygrid-view) - for each month's day headers/cells

**Breaking:** `multiMonthMinWidth` and `multiMonthTitleFormat` are renamed to `singleMonthMinWidth` and `singleMonthTitleFormat`.

```diff
<FullCalendar
- multiMonthMinWidth={200}
+ singleMonthMinWidth={200}

- multiMonthTitleFormat={{ year: 'numeric', month: 'long' }}
+ singleMonthTitleFormat={{ year: 'numeric', month: 'long' }}
/>
```

**Breaking:** `singleMonthMinWidth` (formerly `multiMonthMinWidth`) now INCLUDES padding.


## Resource Timeline View

Includes changes from:
- [Calendar Views (General)](#calendar-views-general)
- [Events (General)](#events-general)

**Breaking:** `resourceAreaColumns` is renamed to `resourceColumns`, and related `*ClassNames` hooks are now `*Class` string settings.

```diff
  <FullCalendar
-   resourceAreaColumns={[
+   resourceColumns={[
      {
        id: 'resource-a',
        title: 'Resource A',
-       headerClassNames: ['my-header', 'my-util'],
+       headerClass: 'my-header my-util',
-       cellClassNames: ['my-cell', 'my-util'],
+       cellClass: 'my-cell my-util',
      }
    ]}

    // will apply to ALL headers
-   resourceAreaHeaderClassNames={['my-header', 'my-util']}
+   resourceColumnHeaderClass='my-header my-util'
-   resourceAreaHeaderContent={(data) => <span>custom content</span>}
+   resourceColumnHeaderContent={(data) => <span>custom content</span>}
-   resourceAreaHeaderDidMount={() => { /* handler */ }}
+   resourceColumnHeaderDidMount={() => { /* handler */ }}
-   resourceAreaHeaderWillUnmount={() => { /* handler */ }}
+   resourceColumnHeaderWillUnmount={() => { /* handler */ }}
  />
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type ColHeaderContentArg } from '@fullcalendar/core'
+ import { type ResourceColumnHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

**Breaking:** In v6, the `resourceGroupLabel*` settings where used to style both row-grouping group headers AS WELL AS column-grouping cells, without any way to distinguish the two from the callbacks themselves. In v7, there are two separate entities for each (`resourceGroupHeader*` and the generic `resourceCell*`). Also, `.groupValue` is killed:

```diff
<FullCalendar
- resourceGroupLabelClassNames={['my-resource-group-header', 'my-resource-group-cell', 'my-util']}
- resourceGroupLabelContent={(data) => (
-   <span>{data.groupValue}</span>
- )}
- resourceGroupLabelDidMount={(data) => { /* handler */ }}
- resourceGroupLabelWillUnmount={(data) => { /* handler */ }}

  // header for grouped rows
+ resourceGroupHeaderClass='my-resource-group-header my-util'
+ resourceGroupHeaderContent={(data) => (
+   <span>{data.fieldValue}</span>
+ )}
+ resourceGroupHeaderDidMount={(data) => { /* handler */ }}
+ resourceGroupHeaderWillUnmount={(data) => { /* handler */ }}

  // grouped cells
+ resourceCellClass={(data) => clsx(
+   !data.resource && // in group?
+     'my-resource-group-cell my-util'
+ )}
+ resourceCellContent={(data) => (
+   <span>{data.fieldValue}</span> // applies to all cells (grouped and non-grouped)
+ )}
+ resourceCellDidMount={(data) => {
+   if (!data.resource) { /* handler for group */ }
+ }}
+ resourceCellWillUnmount={(data) => {
+   if (!data.resource) { /* handler for group */ }
+ }}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type ColCellContentArg } from '@fullcalendar/core'

// header for grouped rows 
+ import { type ResourceGroupHeaderInfo } from '@fullcalendar/react' // or angular/vue3/etc

// grouped cells
+ import { type ResourceCellInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Resource Cell

**Breaking:** `resourceLabel*` has been replaced by generalized `resourceCell*` hooks. If you only want the title cell, branch on `data.field === 'title'`.

```diff
<FullCalendar
- views={{
-   resourceTimeline: {
-     resourceLabelClassNames: ['my-resource-cell', 'my-util'],
-     resourceLabelContent: (data) => (
-       <span>{data.resource.title}</span>
-     ),
-     resourceLabelDidMount: (data) => { /* handler */ },
-     resourceLabelWillUnmount: (data) => { /* handler */ },
-   }
- }}

+ resourceCellClass={(data) => clsx(
+   (data.field === 'title') && 'my-resource-cell my-util'
+ )}
+ resourceCellContent={(data) => (
+   <span>{data.fieldValue}</span> // applies to all cells
+ )}
+ resourceCellDidMount={(data) => {
+   if (data.field === 'title') { /* handler */ }
+ }}
+ resourceCellWillUnmount={(data) => {
+   if (data.field === 'title') { /* handler */ }
+ }}
/>
```

Note that the `resourceLabel*` props ALSO apply to Resource DayGrid/TimeGrid. See those migrations above.

### Resource Lane

**Breaking:** `resourceLaneClassNames` is renamed to `resourceLaneClass` (simple string only), and `resourceLaneContent` is split into `resourceLaneTopContent` and `resourceLaneBottomContent`.

```diff
<FullCalendar
- resourceLaneClassNames={['my-lane', 'my-util']}
+ resourceLaneClass='my-lane my-util'

- resourceLaneContent={(data) => <span>...</span>}
+ resourceLaneTopContent={(data) => <span>...</span>}
+ // or
+ resourceLaneBottomContent={(data) => <span>...</span>}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type ResourceLaneContentArg } from '@fullcalendar/core'
+ import { type ResourceLaneInfo } from '@fullcalendar/react' // or angular/vue3/etc
```

### Group Lane

**Breaking:** `resourceGroupLaneClassNames` is renamed to `resourceGroupLaneClass` (simple string only), and group data now uses `fieldValue` instead of `groupValue`.

```diff
<FullCalendar
- resourceGroupLaneClassNames={['my-group-lane', 'my-util']}
+ resourceGroupLaneClass='my-group-lane my-util'

  resourceGroupContent={(data) => (
-   <span>{data.groupValue}</span>
+   <span>{data.fieldValue}</span>
  )}
/>
```

**Type change:** The parameter given to these callbacks has changed:

```diff
- import { type ColCellContentArg } from '@fullcalendar/core'
+ import { type ResourceGroupLaneInfo } from '@fullcalendar/react' // or angular/vue3/etc
```


## Custom Views

### Custom View via Settings

**Breaking:** In custom view definitions, `.classNames` (array) is replaced by `.viewClass` (single string). Also, the `.content`, `.didMount`, and `.willUnmount` settings have been prefixed with "view":

```diff
  var calendar = new Calendar(calendarEl, {
    views: {
      timeGridFourDay: {
        type: 'timeGrid',
        duration: { days: 4 },

-       classNames: ['my-classname', 'my-util'],
+       viewClass: 'my-classname my-util',

-       content: () => { /* for injecting content */ }
+       viewContent: () => { /* for injecting content */ }

-       didMount: () => { /* handler */ }
+       viewDidMount: () => { /* handler */ }

-       willUnmount: () => { /* handler */ }
+       viewWillUnmount: () => { /* handler */ }
      }
    }
  })
```

**Breaking:** The `buttonText` setting no longer respected. Instead, add to the `buttons` map:

```diff
  var calendar = new Calendar(calendarEl, {
    views: {
      timeGridFourDay: {
        type: 'timeGrid',
        duration: { days: 4 },
-       buttonText: '4 day'
      }
    },
+   buttons: {
+     timeGridFourDay: {
+       text: '4 day'
+     }
+   },
    headerToolbar: {
      center: 'dayGridMonth,timeGridFourDay'
    }
  })
```

### Custom View via JS

**Breaking:** `createPlugin` is no longer required for custom view plugin objects (see [Custom Plugins](#custom-plugins) below).


## Luxon Plugin

**Breaking:** The `@fullcalendar/luxon3` plugin has been removed. Remove it from your `package.json`:

```diff
{
  "dependencies": {
    "@fullcalendar/react": "{latestReleases.v7}",
-   "@fullcalendar/luxon3": "^6.1.20",
    "luxon": "^3.7.2"
  }
}
```

**Breaking:** Because the `@fullcalendar/luxon3` plugin has been removed, the `toLuxonDateTime` and `toLuxonDuration` utilities are no longer available. You can easily replace with direct calls to Luxon:

```diff
- import { toLuxonDateTime, toLuxonDuration } from '@fullcalendar/luxon3'
+ import { DateTime, Duration } from 'luxon'
  
  const locale = 'en-US'
  const timeZone = 'America/New_York'
  
  <FullCalendar
    locale={locale}
    timeZone={timeZone}
    dateClick={(arg) => {
-     const dt = toLuxonDateTime(arg.date, arg.view.calendar)
+     const dt = DateTime.fromJSDate(arg.date, { zone: timeZone, locale })
      ...
    }}
    eventDrop={(arg) => {
-     const dur = toLuxonDuration(arg.delta, arg.view.calendar)
+     const dur = Duration.fromObject(arg.delta, { locale })
      ...
    }}
  />
```

**Breaking:** The `@fullcalendar/luxon3` plugin has been removed, but it's format-string functionality has been moved to a different plugin:

```
npm install --save @fullcalendar/format-luxon3
```

Then replace usage in your code:

```diff
- import luxonPlugin from '@fullcalendar/luxon3'
+ import luxonFormatPlugin from '@fullcalendar/format-luxon3'
  
  <FullCalendar
    titleFormat='LLLL d, yyyy' // a format-string
    plugins={[
-     luxonPlugin,
+     luxonFormatPlugin, // for format-strings only
      ...
    ]}
    ...
  />
```


## Moment Plugins

**Breaking:** The two moment plugin have been removed. Remove them from your `package.json`:

```diff
{
  "dependencies": {
    "@fullcalendar/react": "{latestReleases.v7}",
-   "@fullcalendar/moment": "^6.1.20",
-   "@fullcalendar/moment-timezone": "^6.1.20",
    "moment": "^2.30.1"
  }
}
```

**Breaking:** Because the `@fullcalendar/moment` plugin has been removed, the `toMoment` and `toMomentDuration` utilities are no longer available. You can replace them with direct calls to Moment:

```diff
- import { toMoment, toMomentDuration } from '@fullcalendar/luxon3'
+ import moment from 'moment'
  
  const locale = 'en-US'
  const timeZone = 'America/New_York'
  
  <FullCalendar
    locale={locale}
    timeZone={timeZone}
    dateClick={(arg) => {
-     const mom = toMoment(arg.date, arg.view.calendar)
+     let mom: moment.Moment
+     if (timeZone === 'local') {
+       mom = moment(arg.date)
+     } else if (timeZone === 'UTC') {
+       mom = moment.utc(arg.date)
+     } else {
+       mom = moment.tz(arg.date, timeZone)
+     }
      ...
    }}
    eventDrop={(arg) => {
-     const dur = toMomentDuration(arg.delta, arg.view.calendar)
+     const dur = moment.duration(arg.delta)
      ...
    }}
  />
```

**Breaking:** The `@fullcalendar/moment` plugin has been removed, but it's format-string functionality has been moved to a different plugin:

```
npm install --save @fullcalendar/format-moment
```

Then replace usage in your code:

```diff
- import momentPlugin from '@fullcalendar/moment'
+ import momentFormatPlugin from '@fullcalendar/format-moment'
  
  <FullCalendar
    titleFormat: 'MMMM D, YYYY' // a format-string
    plugins={[
-     momentPlugin,
+     momentFormatPlugin, // for format-strings only
      ...
    ]}
    ...
  />
```


## Custom Plugins

**Breaking:** the `createPlugin` function no longer necessary to prepare a plugin. Just pass-in the object.

```diff
- const customViewPlugin = createPlugin({
+ const customViewPlugin = {
    name: 'custom-view',
    views: {
      custom: CustomViewConfig
    }
+ };
- });
  
  let calendar = new Calendar(calendarEl, {
    plugins: [customViewPlugin],
  });
```

**Type change:**

```diff
- import { PluginDef, PluginDefInput } from '@fullcalendar/core'
+ import { PluginInput } from '@fullcalendar/react' // or angular/vue3/etc
```


## Custom Theme Class

**Breaking:** V6 had an poorly-documented API for theming that was rarely used by developers. It has been replaced by the miriad of `*Class` and `*Content` settings now available in v7. Here's how to migrate:

<div class='diff-code-2col' markdown='1'>
<div>

```diff
- import {
-   StandardTheme,
-   createPlugin
- } from '@fullcalendar/core'
  
- class CustomTheme extends StandardTheme {
-   root = 'my-root'

    // toolbar
-   buttonGroup = 'my-button-group'
-   button = 'my-button'
-   buttonActive = 'my-button-selected'

    // popover
-   popover='my-popover'
-   popoverHeader='my-popover-header'
-   popoverContent='my-popover-body'

    // affects multiple elements
-   tableCellShaded = 'my-cell-shaded'
- }
  
  <FullCalendar
-   plugins={[
-     createPlugin({
-       name: 'custom-theme',
-       themeClasses: {
-         custom: CustomTheme,
-       }
-     })
-   ]}
-   theme='custom'
```

</div>
<div>

```diff
  <FullCalendar

+   className='my-root'

    // toolbar
+   buttonGroupClass='my-button-group'
+   buttonClass={(data) => clsx(
+     'my-button',
+     data.isSelected && 'my-button-selected',
+   )}

    // popover
+   popoverClass='my-popover'
+   dayHeaderClass={(data) => clsx(
+     data.inPopover && 'my-popover-header'
+   )}
+   dayCellClass={(data) => clsx(
+     data.inPopover && 'my-popover-body'
+   )}

    // affects multiple elements
+   allDayDividerClass='my-cell-shaded'
+   listDayHeaderClass='my-cell-shaded'
+   resourceColumnDividerClass='my-cell-shaded'
+   resourceGroupHeaderClass='my-cell-shaded'
+   resourceGroupLaneClass='my-cell-shaded'
  />
```

</div>
</div>

The following members have been removed because class-based glyph icons are now discouraged:

```diff
- CustomTheme.prototype.baseIconClass
- CustomTheme.prototype.iconClasses
- CustomTheme.prototype.rtlIconClasses
- CustomTheme.prototype.iconOverridePrefix
```


## Locales

**Breaking:** The Shape of a locale object has changed:

<div class='diff-code-2col' markdown='1'>
<div>

```diff
  {
    code: 'es',
-   buttonText: {
-     prev: 'Ant',
-     next: 'Sig',
-     today: 'Hoy',
-     year: 'Año',
-     month: 'Mes',
-     week: 'Semana',       // "long" value
-     day: 'Día',
-     list: 'Agenda',
-   },
-   weekTextLong: 'Semana', // "long" value
-   weekText: 'Sm',         // "short" value
-   buttonHints: {
-     prev: '$0 antes',
-     next: '$0 siguiente',
-     today: (unitText) => { /* ... */ }
-   },
  }
```

</div>
<div>

```diff
  {
    code: 'es',
  
+   prevText: 'Ant',
+   nextText: 'Sig',
+   todayText: 'Hoy',
+   yearText: 'Año',
+   monthText: 'Mes',
+   weekText: 'Semana',  // "long" value
+   weekTextShort: "Sm", // "short" value
+   dayText: 'Día',
+   listText: 'Agenda',
  
  
+   prevHint: '$0 antes',
+   nextHint: '$0 siguiente',
+   todayHint: (unitText) => { /* ... */ }

  }
```

</div>
</div>

Note the changes in week-related settings:

- `weekText` -> `weekTextShort` (ex: `"W"`)
- `weekTextLong` -> `weekText` (ex: `"Week"`)


## Interactions

**Breaking:** removed `fixedMirrorParent` setting. Element dragging "mirror" elements will always be attached to `<body>` now:

```diff
<FullCalendar
- fixedMirrorParent={document.body} // unnecessary now
/>
```

**Type change:** For all names, the `Arg` postfix replaced with `Info`:

<div class='diff-code-2col' markdown='1'>
<div>

```diff
- import {
-   DateSelectArg,
-   DateUnselectArg,
-   EventClickArg,
-   EventAddArg,
-   EventChangeArg,
-   EventRemoveArg,
- } from '@fullcalendar/core'

- import {
-   DateClickArg,
-   EventDragStartArg,
-   EventDragStopArg,
-   EventDropArg,
-   EventResizeStartArg,
-   EventResizeStopArg,
-   EventResizeDoneArg,
-   EventHoveringArg,
-   DropArg,
-   EventReceiveArg,
-   EventLeaveArg,
- } from '@fullcalendar/interaction'
```

</div>
<div>

```diff
+ import {
+   DateSelectInfo,
+   DateUnselectInfo,
+   EventClickInfo,
+   EventAddInfo,
+   EventChangeInfo,
+   EventRemoveInfo,
+ } from '@fullcalendar/react'
                      // or angular/vue3/etc
+ import {
+   DateClickInfo,
+   EventDragStartInfo,
+   EventDragStopInfo,
+   EventDropInfo,
+   EventResizeStartInfo,
+   EventResizeStopInfo,
+   EventResizeDoneInfo,
+   EventHoveringInfo,
+   DropInfo,
+   EventReceiveInfo,
+   EventLeaveInfo,
+ } from '@fullcalendar/react/interaction'
                      // or angular/vue3/etc
```

</div>
</div>


## Misc

**Type change:** The argument given to the `datesSet` callback has changed:

```diff
- import { DatesSetArg } from '@fullcalendar/core'
+ import { DatesSetInfo } from '@fullcalendar/react' // or angular/vue3/etc
```


<!-- END -->
</div>
</div>
