← Back

FullCalendar Web Component (Vaadin 10)

FullCalendar integration




FullCalendar integration

This addon is an integration of the FullCalendar (v3.10.0) as Flow component for Vaadin Platform / Vaadin 10-13 (and 14 in Bower compatibility mode).

This version is not maintained any more. I recommend you to use the latest version for Vaadin 14+. It can be found here:

For information about the FullCalendar (functionality, features, license information, etc.) visit

If you want to use the Scheduler, please have a look here:

Addon Functionality

The following functions are implemented and available to use from server side:

  • adding / updating / removing calendar items,

  • switching between shown intervals (next month, previous month, etc.),

  • goto a specific date or today,

  • switch the calendar view (month, basic views for days and weeks, agenda views for days and weeks, list views for day to year),

  • setting a locale to be used for displaying week days, formatting values, calculating the first day of the week, etc. (supported locales are provided as constant list)

  • setting the first day of week to be shown (overrides the locale setting),

  • setting the height of the calendar instance (calculated by parent, aspect ratio or fixed pixel size)

  • show of week numbers

  • limit max shown entries per day (except basic views)

  • showing now indicator

  • activating day / week numbers / names to be links

  • styles are overridable via custom properties

  • setting a eventRender JS function from server side

  • setting business hours (multiple instances possible)

  • timezone support

  • Event handling for

    • clicking an empty time spot in the calendar,
    • selecting a block of empty time spots in the calendar,
    • clicking an entry,
    • moving an entry via drag and drop (event is fired on drop + changed time),
    • resizing an entry (event is fired after resize + changed time),
    • view rendered (i. e. to update a label of the shown interval)
    • clicking on limited entries link "+ X more"
    • clicking on a day's or week's number link (when activated)
  • Model supports setting

    • title,
    • start / end / all day flag,
    • color (html colors, like "#f00" or "red"),
    • description (not shown via FC),
    • editable / read only
    • rendering mode (normal, background, inversed background)


Starting with 1.6.0 FC supports setting timezones. From this version, entries and some events work with Instant to represent the time based on UTC. You may set a custom timezone to display events for a user's timezone while the entries themselves still work with UTC based times.

Feedback and co.

If there are bugs or you need more features (and I'm not fast enough) feel free to contribute on GitHub. :) I'm also happy for feedback or suggestions about improvements.

Sample code

// Create a new calendar instance and attach it to our layout
FullCalendar calendar = FullCalendarBuilder.create().build();
container.setFlexGrow(1, calendar);

// Create a initial sample entry
Entry entry = new Entry();
entry.setTitle("Some event");
entry.setStart(, 0));

 * The day click event listener is called when a user clicks in an empty space inside of the 
 * calendar. Depending of if the clicked point was a day or time slot the event will provide the 
 * time details of the clicked point. With this info you can show a dialog to create a new entry.
calendar.addDayClickListener(event -> {
        Optional<LocalDateTime> optionalDateTime = event.getClickedDateTime();
        Optional<LocalDate> optionalDate = event.getClickedDate();

        Entry entry = new Entry();
        if (optionalDateTime.isPresent()) { // check if user clicked a time slot
            LocalDateTime time = optionalDateTime.get();

        } else if (optionalDate.isPresent()) { // check if user clicked a day slot
            LocalDateTime date = optionalDate.get().atStartOfDay();


        new EntryDialog(calendar, entry, true).open();

 * The entry click event listener is called when the user clicks on an existing entry. 
 * The event provides the clicked event which might be then opened in a dialog.
calendar.addEntryClickListener(event -> 
    new DemoDialog(calendar, event.getEntry(), false).open());
// ... create a form and binder to provide editable components to the user

HorizontalLayout buttons = new HorizontalLayout();
Button buttonSave;
if (newInstance) {
    buttonSave = new Button("Create", e -> {
        if (binder.validate().isOk()) {
            // add the entry to the calendar instance
} else {
    buttonSave = new Button("Save", e -> {
        if (binder.validate().isOk()) {
             // update an existing entry in the client side

if (!newInstance) {
    Button buttonRemove = new Button("Remove", e -> calendar.removeEntry(entry));
private void init() {
    // The element that should show the current interval. 
    HasText intervalLabel = new Span();

    // combo box to select a view for the calendar, like "monthly", "weekly", ...
    ComboBox<CalendarView> viewBox = new ComboBox<>("", CalendarViewImpl.values());
    viewBox.addValueChangeListener(e -> {
        CalendarView value = e.getValue();
        calendar.changeView(value == null ? CalendarViewImpl.MONTH : value);

      * The view rendered listener is called when the view has been rendererd on client side 
      * and FC is aware of the current shown interval. Might be accessible more directly in 
      * future.
    calendar.addViewRenderedListener(event -> 
        LocalDate intervalStart = event.getIntervalStart();
        CalendarView cView = viewBox.getValue();

        String formattedInterval = ... // format the intervalStart based on cView 

// #1 setting a fixed height

// #2 setting a auto height - this is calculated by the w-h-ratio of the calendar

// #3 calculate height by parent. parent is a block container.

// #4 calculate height by parent + usage of css calc(). parent is a block container.
calendar.getElement().getStyle().set("height", "calc(100vh - 450px)");

// #5 calculate height by parent. parent is a flex container.
calendar.getElement().getStyle().set("flex-grow", "1");
1. Copy the styles.html from the github demo or create your own 
custom style file and place it in your applications webapp/frontend 
folder (e. g. webapp/frontend/styles/styles

The github demo file can be obtained from here:

2. Modify the styles as needed.
            /* light blue to be used instead of default light yellow*/
            --fc-unthemed_tdfc-today-background: #81DAF5 !important;
            /* and some fancy border */
            --fc_td-border-style: dotted !important;
            --fc_td-border-width: 2px !important;

3. Use the styles file in your application.
public class FullCalendarApplication extends Div {
    // ...
// The given string will be interpreted as js function on client side
// and attached as eventRender callback. 
// Make sure, that it does not contain any harmful code.

calendar.setEntryRenderCallback("" +
        "function(event, element) {" +
        "   console.log(event.title + 'X');" +
        "   element.css('color', 'red');" +
        "   return element; " +
1. Create a new polymer template (e.g. webapp/frontend/my-full-calendar.html):

<link rel="import" href="bower_components/fullcalendar/full-calendar.html">
<dom-module id="my-full-calendar">
        class MyFullCalendar extends FullCalendar {
            static get is() {
                return 'my-full-calendar';

            _createInitOptions() {
                var options = super._createInitOptions();
                options.eventRender = function (event, element) {
                    element.css('color', 'red');
                    return element;
                return options;

        customElements.define(, MyFullCalendar);

2. Create a subclass of FullCalendar 

import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.HtmlImport;
import org.vaadin.stefan.fullcalendar.FullCalendar;

public class MyFullCalendar extends FullCalendar {
    MyFullCalendar(int entryLimit) {

3. Use this class in your code

calendar = new MyFullCalendar(5);
Entry entry = new Entry();
// ... setup entry details

// Single instance for "normal" business week (mo-fr)
calendar.setBusinessHours(new BusinessHours(LocalTime.of(9, 0), LocalTime.of(17, 0),BusinessHours.DEFAULT_BUSINESS_WEEK));

// Multiple instances
                new BusinessHours(LocalTime.of(9, 0), LocalTime.of(17, 0),BusinessHours.DEFAULT_BUSINESS_WEEK),
                new BusinessHours(LocalTime.of(12, 0), LocalTime.of(15, 0), DayOfWeek.SATURDAY)
// Single instance for "each day from 9am to midnight"
calendar.setBusinessHours(new BusinessHours(LocalTime.of(9, 0)));
// Per default, our FC works with UTC. You can set a custom timezone to be shown for the user. 
// This will automatically update all entries on the client side.
Timezone tzBerlinGermany = new Timezone("Europe/Berlin");

// We can also reset the timezone to default.

// We can also read the browsers timezone, after the component has been attached to the client side.
// There are other ways to obtain the browser's timezone, so you are not obliged to use the listener.
calendar.addBrowserTimezoneObtainedListener(event -> calendar.setTimezone(event.getTimezone()));

// When using timezones, entries can calculate their start and end in different ways.
entry.setStart(; // UTC 
entry.setStart(, tzBerlinGermany); // timezone is used to calculate the UTC value

entry.setCalendar(calendar); // is done automatically, when using calendar.addEntry(entry);
entry.setStart(; // Uses the calendars timezone (or UTC as fallback)
// Timezone provides some convenient methods to work with the two different temporal types
tzBerlinGermany.convertToUTC(LocalDateTime.of(2018, 10, 1, 10, 0, 0)) // Standard time, returns Instant for 9:00 UTC this day.
tzBerlinGermany.convertToUTC(LocalDateTime.of(2018, 8, 1, 10, 0, 0)) // Summer time, returns Instant for 8:00 UTC this day.
tzBerlinGermany.convertToLocalDateTime( // returns a date time with +1/+2 hours (depending on summer time).
Create a custom component, that extends FullCalendar or FullCalendarScheduler. Add a style element and your custom css stylings. Override the static template method and let it insert your template with the style node into the parents template DOM.

The following example sets a beautiful green background for empty lists (.fc-list-empty). Please note, that the id of your component inside of the template method needs to be updated to your component's id.

<link rel="import" href="bower_components/fullcalendar/full-calendar-scheduler.html">

<dom-module id="my-full-calendar">
        <style id="styles">
            .fc-list-empty {
                background-color: green !important;

        class MyFullCalendar extends FullCalendarScheduler {
            static get is() {
                return 'my-full-calendar';

            // example of adding / overriding styles 
            static get template() {
                const parentTemplate = FullCalendarScheduler.template.cloneNode(true);
                const childTemplate = Polymer.DomModule.import('my-full-calendar', 'template');

                parentTemplate.content.insertBefore(childTemplate.content, parentTemplate.content.firstChild);

                return parentTemplate;

        customElements.define(, MyFullCalendar);



(Loading compatibility data...)

Was this helpful? Need more help?
Leave a comment or a question below. You can also join the chat on Discord or ask questions on StackOverflow.


  • Business hours now maps DayOfWeek.SUNDAY to 0
MIT License


Vaadin 10
Vaadin 10+
Vaadin 11
Vaadin 11+
Vaadin 12
Vaadin 13
Vaadin 12+ in 1.8.0
Google Chrome
iOS Browser
Android Browser
Windows Phone
Microsoft Edge