V5 Release Notes and Upgrade Guide
Getting the Code
You can install with npm just like in v4:
npm install --save @fullcalendar/core @fullcalendar/daygrid
Or, you can use one of the new pre-built bundles (see below)
Virtual DOM
FullCalendar now internally uses a miniature virtual-DOM library called Preact. Aside from making the codebase more maintainable, it makes FullCalendar more performant to end-users. DOM manipulations and page reflows are kept to a minimum. This is especially true for rerendering events. FullCalendar no longer needs to rerender ALL events when just one event changes. It rerenders only what it needs to (#3003).
Just because we use a virtual DOM doesn’t mean we no longer think about performance. We still care about limiting the amount of rerender execution, even though it performs fewer real DOM operations. This will continue to be a priority as we further develop FullCalendar.
How does this affect FullCalendar’s API? It doesn’t really. From any of the content injection options like eventContent
you are able to construct and return a virtual DOM node. Learn more in this article. Aside from that, you won’t need to think about the virtual DOM.
Real React
The @fullcalendar/react
package is no longer merely a connector. It leverages the actual React virtual DOM engine the rest of your React app uses. It swaps out the Preact rendering engine it normally uses for real React, so you can take advantage of Fiber. This is sourcery that Adam will likely blog about in the future.
CSS and DOM Structure
Firstly, the DOM structure of the calendar has been simplified quite a bit. There is less nesting. Month view and daygrid view have particularly benefitted from this. Each row is represented by a single <tr>
and events are rooted in individual <td>
cells, whereas in v4 we had many tables-within-tables (addresses #2853)
The CSS has been completely rewritten. Most importantly, the selectors are flatter. Instead of a selector like .fc-event .fc-title
we now use something like .fc-event-title
. This results in fewer selectors battling for precedence and makes styling easier to override.
As a result, if you’ve written custom styling, it will most likely need to be rewritten for v5, or at the very least you will need to swap out your classNames. To help you do this, we will likely release a className-upgrade document prior to the official release.
CSS variables are a new feature. They allow you to manipulate fullcalendar’s CSS in a more surgical way than simply overriding existing rules. Some build-system magic is required for this. Read more on how to use CSS variables »
CSS Importing
In v4, it was your responsibility to import all of fullcalendar’s stylesheets. You may have done this in one your project’s SASS files. Or, if you had a build system that handled CSS, you may have done this from your JavaScript.
In v5, you no longer need to do this! The plugins will import their own stylesheets (with the exception of the bootstrap plugin). So, you’ll be able remove lines like these:
import { Calendar } from '@fullcalendar/core'
import timeGridPlugin from '@fullcalendar/timegrid'
// DON'T DO THIS ANYMORE! it will happen automatically
// import '@fullcalendar/core/main.css';
// import '@fullcalendar/daygrid/main.css'; // a dependency of timegrid
// import '@fullcalendar/timegrid/main.css';
let calendar = new Calendar(calendarEl, {
plugins: [ timeGridPlugin ]
// other options...
})
HOWEVER, you’ll need a build system that is able to handle CSS. Some popular ones:
- for Webpack you’ll need css-loader
- for Rollup you can use rollup-plugin-postcss
- for Parcel you won’t need to do anything, it’s supported by default
Configuring your build system to handle CSS is beyond the scope of this document. There are many examples out there.
If you don’t use a build system, meaning you use manual <script>
tags and browser globals, then please read the next section…
Pre-built Bundles
What if you want to avoid using a build system? What if you prefer manual <script>
tags and browser globals? This is why we are beginning to offer pre-built bundles of plugins (#4566). In fact, using the pre-built bundles will be the ONLY way to use manual <script>
tags going forward. The individual plugins will no longer provide browser-runnable UMD files.
First, get the bundle distro files on the Getting Started page »
To use a bundle, do something like this:
<link ref='fullcalendar/main.css' rel='stylesheet' />
<script src='fullcalendar/main.js'></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar')
var calendar = new FullCalendar.Calendar(calendarEl, {
// plugins: [ 'dayGrid' ] // DON'T DO THIS ANYMORE!
})
calendar.render()
})
</script>
You’ll still need to include the CSS file. You won’t need to define the plugins
array anymore.
For initializing scheduler, do something like this:
<link ref='fullcalendar-scheduler/main.css' rel='stylesheet' />
<script src='fullcalendar-scheduler/main.js'></script><!-- only one JS file. don't include the other bundle -->
<script>
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar')
var calendar = new FullCalendar.Calendar(calendarEl, {
// plugins: [ 'resourceTimeline' ] // DON'T DO THIS ANYMORE!
})
calendar.render()
})
</script>
When using the scheduler bundle, you don’t need to include both the standard bundle AND the scheduler bundle. The scheduler bundle already INCLUDES the standard plugins.
Better Printer-Friendly Rendering
Rendering for print has been greatly improved, especially for timeline view (#4813). You must use the new @fullcalendar/adaptive
plugin, which conditionally renders the calendar when the user clicks Print/Print-Preview in their browser. This new plugin is a premium plugin.
Learn more about rendering for print
Toolbar
|
|
|
|
When specifying headerToolbar and footerToolbar, the { left, center, right }
properties are still available to you. However, the following properties have been added to better support RTL locales:
start
- if the calendar is left-to-right, it will appear on the left. if right-to-left, it will appear on the right.end
- if the calendar is left-to-right, it will appear on the right. if right-to-left, it will appear on the left.
View
|
|
|
Use the view render hooks instead:
|
View object members |
|
See below for changes to how Custom JS Views are implemented.
Current Date
|
|
Date Rendering
The header elements above the day cells in daygrid and timegrid views. Also, the title elements for each day in list view. For the timeline view, jump down to slot rendering.
|
|
|
|
|
Use the day-header render hooks instead:
|
The days cells in daygrid and timegrid views:
|
Use the day-cell render hooks instead:
|
|
The horizontal time slots in timegrid view or the vertical datetime slots in timeline view. In timeline view, even though these slots might represent distinct days, they are still considered "slots" as opposed to "day cells".
|
|
|
|
|
|
The following slot render hooks are now available:
|
|
You can now customize the long span of content next to the slot's date/time text. In timegrid view, this is the horizontal space that passes under all of the days. In timeline view, this is the vertical space that passes through the resources. Use the following slot render hooks:
|
|
timeline slot classNames |
In timeline view, slots now have more descriptive classNames like
|
Dates, in aggregate:
|
|
|
No direct replacement. For rendering, handle individual date cells via dayHeaderWillUnmount, dayCellWillUnmount, or slotLabelWillUnmount. |
Week numbers:
|
This is now the default behavior. The |
|
|
|
|
default week number formatting |
Week numbers were previously formatted as plain numeric values like "6". Now, by default, they are formatted with their weekText prefix, so they'll look like "W6". To change back to the old behavior, change weekNumberFormat to |
The following week-number render hooks are now available:
|
The area where the “all-day” text is displayed, both in timegrid view and list view:
|
Use the all-day render hooks instead:
|
Event Rendering
|
Use the new event render hooks instead:
|
|
No direct replacement. You can use eventDidMount to know when the element has been inserted into the DOM, but it's no longer possible to know when its position has stabilized. |
|
No direct replacement. |
|
Call Calendar::render after initialization to rerender everything. |
|
|
|
These settings have been renamed to display and eventDisplay respectively. They accept:
|
daygrid events |
By default, single-day timed events in daygrid will render with a dot as opposed to a solid filled rectangle. To revert to the old behavior, set the calendar-wide eventDisplay option to |
timegrid events |
New feature: as the user scrolls down the timegrid, event title was still visible via sticky positioning.
Styling change: the resizer on the bottom of the event is no longer styled with a |
background events |
Background events now display their title (addresses #2746). They previously did not. To prevent this, don’t assign a
|
event classNames |
Event elements now have more descriptive classNames about what dates they span. For example, |
Event Manipulation
Can now specify |
|
A |
|
Within the given argument, the |
|
Both now receive a |
|
Methods for monitoring changes in event data: |
|
Event sources |
|
Event methods |
For serialization:
|
Event properties |
A convenient way to get the start/end dates of an Event Object in ISO8601 string form:
|
More Events Popover
When there are too many events to fit within a single day:
|
|
|
|
|
|
|
Use the more link render hooks instead:
|
Resource Rendering
The default value will now force resources to be sorted by |
|
|
A resource "label" is anywhere the name of a resource is displayed. They exist in the header of vertical resource view and the side section of resource timeline view. Use the following resource render hooks going forward:
|
A resource "lane" is an element in resource-timeline view. It runs horizontally across the timeline slots for each resource. The following resource render hooks are now available:
|
|
|
Call Calendar::render after initialization to rerender everything. |
When resources are grouped together in resource-timeline view:
|
A resource group "label" is where a group's name is displayed. Use the following resource group render hooks going forward:
|
A resource group "lane" is the horizontal area running along the time slots. The following resource group render hooks are now available:
|
The area on the side of resource-timeline view that contains resource names and data.
|
The "resource-area header" is above the resource data and displays the text "Resources" by default. It was previously called the "resource label", a term which is now being used to describe something else! When resourceAreaColumns is activated, it will not be displayed. Use the resource-area header render hooks going forward:
|
|
|
Properties within resourceColumns:
|
Renamed to these properties within resourceAreaColumns:
|
Properties within resourceColumns:
|
Renamed to these properties within resourceAreaColumns:
|
Resource Manipulation
Methods for monitoring changes in resource data: |
|
|
|
Resource methods |
Setters: For serialization:
|
List View Rendering
In list view, the “No events to display” message.
|
|
|
The following render hooks are now available:
|
Now Indicator Rendering
The following render hooks are now available for customizing the now indicator:
|
Calendar Sizing
No longer accepts a function. Reassign imperatively via setOption. |
|
No longer accepts a function. Reassign imperatively via setOption.
No longer accepts the |
|
New settings related to stickiness:
|
Event Data
|
In v5,
|
Event Sources
|
|
Locales
|
|
Custom JS Views
Custom views written as JavaScript classes will need to be refactored to work. Subclasses of View
are no longer accepted. Instead, you specify a plain configuration object.
In the old way, you had different methods that were called when different pieces of data changed:
class CustomView extends View { // a class. this is the OLD way
renderSkeleton() {
this.el.classList.add('custom-view')
this.el.innerHTML =
'<div class="view-title"></div>' +
'<div class="view-events"></div>'
}
renderDates(dateProfile) {
this.el.querySelector('.view-title').innerHTML = dateProfile.currentRange.start.toUTCString()
}
renderEvents(eventStore) {
this.el.querySelector('.view-events').innerHTML = Object.keys(eventStore).length + ' events'
}
}
Now, the content
function gets called when ANY change occurs:
const CustomViewConfig = { // a plain object. this is the NEW way
classNames: [ 'custom-view' ],
content: function(props) {
let html =
'<div class="view-title">' +
props.dateProfile.currentRange.start.toUTCString() +
'</div>' +
'<div class="view-events">' +
Object.keys(props.eventStore).length + ' events' +
'</div>'
return { html: html }
}
}
You can return any of the available content injection formats such as HTML, real DOM nodes, or virtual DOM nodes.
If you want to maintain state across calls to content
, you are better off writing a Preact/React component instead. More information »
Interaction Plugin
The @fullcalendar/interaction
plugin’s browser globals have changed:
new FullCalendarInteraction.Draggable(settings) // the OLD way
new FullCalendar.Draggable(settings) // the NEW way
Moment Plugin
The @fullcalendar/moment
package’s ES6 exports have changed:
// OLD
import { toDuration } from '@fullcalendar/moment'
// NEW
import { toMomentDuration } from '@fullcalendar/moment'
Also, when used with Webpack, the @fullcalendar/moment
package will now import ALL locale data, which most people will not need. This was not the case in v4. To avoid this, install a Webpack plugin specifically for this purpose. More info.
Moment Timezone Plugin
When used with Webpack, the @fullcalendar/moment-timezone
package will now import ALL timezone data, which most people will not need. This was not the case in v4. To avoid this, install a Webpack plugin specifically for this purpose. More info.
Luxon Plugin
The @fullcalendar/luxon
package can no longer be used with script tags and browser globals. It can ONLY be used used with an ES6 build system.
Also, the package’s ES6 exports have changed:
// OLD
import { toDateTime, toDuration } from '@fullcalendar/luxon'
// NEW
import { toLuxonDateTime, toLuxonDuration } from '@fullcalendar/luxon'
Other Misc Changes
- feature: you can force rerendering of anything on the calendar by calling the
Calendar::render
method again after initialization - feature: full sourcemaps included for each NPM package (#4719)
- fix: timeline event drag/resize when on second line, pops to top (#4893)
- fix: timeline scrolling sometimes gets out of sync when using a scroll wheel (#4889)
- fix:
rerenderDelay
causes selectable and editable lag (#4770) - fix: CSP doesn’t allow setting of inline CSS (#4317)
- fix: when
eventSourceSuccess
callback throws error, looks like JSON parsing failed (#4947) - fix: always show more-link when supplying
0
(#2978)
React Connector
When using the React connector, you can now return virtual DOM nodes to customize rendering (react#12):
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
export const DemoApp(props) => (
<div className='wrapper'>
<FullCalendar
plugins={[dayGridPlugin]}
eventContent={(arg) => (
<div class='custom-event-content'>
<b>{arg.timeText}</b>
<i>{arg.event.title}</i>
</div>
)}
/>
</div>
)
Also, any utilities that normally would be accessed via @fullcalendar/core
can now be accessed via @fullcalendar/react
. This will prevent you from needing to import the core package. More info »
Vue Connector
Previouly, when using the Vue connector, you specified each option as its own attribute in your template:
<!-- the old way -->
<FullCalendar
:weekends='false'
@dateClick='handleDateClick'
/>
Now, you specify them as a single root options object:
<!-- the new way -->
<FullCalendar
:options='calendarOptions'
/>
Of course you’ll need to create this object somewhere:
const AppComponent = {
data: function() {
return {
calendarOptions: {
weekends: false,
dateClick: this.handleDateClick
}
}
},
methods: {
handleDateClick: function(arg) {
alert('clicked on ' + arg.dateStr)
}
}
}
This results in less duplication between your Vue component’s JS and template (vue#47). It also thankfully blurs the distinction between props and “handlers” for which you’d need to use v-on
or @
. All properties are treated equally, resuling in a simpler API.
You now have the ability to customize rendering with the use of Vue scoped slots (addresses vue#14):
<FullCalendar :options='calendarOptions'>
<template v-slot:eventContent='arg'>
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
</template>
</FullCalendar>
This is possible with any of the *Content
options in the API.
Also, any utilities that normally would be accessed via @fullcalendar/core
can now be accessed via @fullcalendar/vue
. This will prevent you from needing to import the core package. More info »
Also,
- feature: included TypeScript definitions (vue#31)
- fix: Class instances in extendedProps are converted to plain objects (vue#53)
Angular Connector
Firstly, the Angular connector now requires Angular 9.
In v4, when using the Angular connector, you specified each option as its own attribute in your template:
<!-- the old way -->
<full-calendar
[weekends]="false"
(dateClick)="handleDateClick"
></full-calendar>
Now, you specify them as a single root options object:
<!-- the new way -->
<full-calendar
[options]="calendarOptions"
></full-calendar>
Of course you’ll need to create this object somewhere:
class AppComponent {
calendarOptions = {
weekends: false,
dateClick: this.handleDateClick.bind(this) // binding is important!
}
handleDateClick(arg) {
alert('clicked on ' + arg.dateStr)
}
}
This results in less duplication between your Angular component’s JS and template. It also thankfully blurs the distinction between props and “handlers” for which you’d need to use (parentheses)
instead of [brackets]
. All properties are treated equally, resuling in a simpler API.
Also, any utilities that normally would be accessed via @fullcalendar/core
can now be accessed via @fullcalendar/angular
. This will prevent you from needing to import the core package. More info »
Upgrading from V3
Many developers will be upgrading from v3 instead of v4. We will likely release a separate guide for this process before the official v5 is released. In the meantime, here are some tips for upgrading from v3 -> v5 in lieu of a full guide:
- Follow the v3 -> v4 upgrade guide but ignore the following areas:
- “Initialization” and anything related to
<script>
tags or stylesheets - anything related to content injection, such as options with the words
render
,text
, orhtml
in them
- “Initialization” and anything related to
- Learn how to install and initialize a v5 calendar from the Getting Started article.
- Follow this v4 -> v5 upgrade guide afterwards.