Calendar (FullCalendar)

The Most Popular JavaScript Calendar.

FullCalendar documentation share-external-link-1

How to use

Import the files you need for your calendar from FullCalendar. The core JavaScript file is required and one of the grid plugins, e.g. daygrid. We also recommend you import moment.js and helpers file too.

(async () => {
    const {Calendar} = await import(/* webpackChunkName: 'fullcalendar.core' */ '@fullcalendar/core');

    const [{default: dayGridPlugin}, {default: timeGridPlugin}, {default: listPlugin}, {default: interactionPlugin, Draggable}, {default: bootstrap5Plugin}, {default: moment}, helper] =
        await Promise.all([
            import(/* webpackChunkName: 'fullcalendar.daygrid' */ '@fullcalendar/daygrid'),
            import(/* webpackChunkName: 'fullcalendar.timegrid' */ '@fullcalendar/timegrid'),
            import(/* webpackChunkName: 'fullcalendar.list' */ '@fullcalendar/list'),
            import(/* webpackChunkName: 'fullcalendar.interaction' */ '@fullcalendar/interaction'),
            import(/* webpackChunkName: 'fullcalendar.bootstrap5' */ '@fullcalendar/bootstrap5'),
            import(/* webpackChunkName: 'moment' */ 'moment'),
            import(/* webpackChunkName: 'helpers' */ '../helpers')
        ]);
})();

Basic usage

<div id="fullcalendarSimple" class="h-1000px"></div>
const calendarSimpleEl = document.getElementById('fullcalendarSimple');

if(calendarSimpleEl) {
    (async () => {
        const {Calendar} = await import(/* webpackChunkName: 'fullcalendar.core' */ '@fullcalendar/core');

        const [{default: dayGridPlugin}, {default: timeGridPlugin}, {default: listPlugin}, {default: interactionPlugin, Draggable}, {default: bootstrap5Plugin}, {default: moment}, helper] =
            await Promise.all([
                import(/* webpackChunkName: 'fullcalendar.daygrid' */ '@fullcalendar/daygrid'),
                import(/* webpackChunkName: 'fullcalendar.timegrid' */ '@fullcalendar/timegrid'),
                import(/* webpackChunkName: 'fullcalendar.list' */ '@fullcalendar/list'),
                import(/* webpackChunkName: 'fullcalendar.interaction' */ '@fullcalendar/interaction'),
                import(/* webpackChunkName: 'fullcalendar.bootstrap5' */ '@fullcalendar/bootstrap5'),
                import(/* webpackChunkName: 'moment' */ 'moment'),
                import(/* webpackChunkName: 'helpers' */ '../helpers')
            ]);

        let todayDate = moment().startOf('day'),
            thisMonth = todayDate.format('YYYY-MM');

        let calendarSimple = new Calendar(calendarSimpleEl, {
            themeSystem: 'bootstrap5',
            plugins: [dayGridPlugin, bootstrap5Plugin],
            initialView: 'dayGridMonth',
            headerToolbar: {
                left: null,
                center: 'title',
                right: 'prev,next customToday'
            },
            buttonText: {
                today: 'Today',
                prev: 'Prev',
                next: 'Next'
            },
            customButtons: {
                customToday: {
                    text: 'Today',
                    click: () => {
                        calendarSimple.today();
                    }
                }   
            },
            events: [
                {
                    title: 'Interview',
                    start: thisMonth + '05T10:30:00',
                    end: thisMonth + '05T13:30:00',
                    className: 'bg-primary'
                }
            ]
        });
        
        calendarSimple.render();
    })();
}

Drag & Drop events

<div id="fullcalendarDraggable" class="h-1000px"></div>
const calendarDraggableEl = document.getElementById('fullcalendarDraggable');

if(calendarDraggableEl) {
    (async () => {
        const {Calendar} = await import(/* webpackChunkName: 'fullcalendar.core' */ '@fullcalendar/core');

        const [{default: dayGridPlugin}, {default: timeGridPlugin}, {default: listPlugin}, {default: interactionPlugin, Draggable}, {default: bootstrap5Plugin}, {default: moment}, helper] =
            await Promise.all([
                import(/* webpackChunkName: 'fullcalendar.daygrid' */ '@fullcalendar/daygrid'),
                import(/* webpackChunkName: 'fullcalendar.timegrid' */ '@fullcalendar/timegrid'),
                import(/* webpackChunkName: 'fullcalendar.list' */ '@fullcalendar/list'),
                import(/* webpackChunkName: 'fullcalendar.interaction' */ '@fullcalendar/interaction'),
                import(/* webpackChunkName: 'fullcalendar.bootstrap5' */ '@fullcalendar/bootstrap5'),
                import(/* webpackChunkName: 'moment' */ 'moment'),
                import(/* webpackChunkName: 'helpers' */ '../helpers')
            ]);

        let todayDate = moment().startOf('day'),
            yesterday = todayDate.clone().subtract(1, 'day').format('YYYY-MM-DD'),
            today = todayDate.format('YYYY-MM-DD'),
            tomorrow = todayDate.clone().add(1, 'day').format('YYYY-MM-DD'),
            thisMonth = todayDate.format('YYYY-MM');

        let calendarDraggable = new Calendar(calendarDraggableEl, {
            themeSystem: 'bootstrap5',
            plugins: [dayGridPlugin, interactionPlugin, bootstrap5Plugin],
            initialView: 'dayGridMonth',
            headerToolbar: {
                left: null,
                center: 'title',
                right: 'prev,next customToday'
            },
            buttonText: {
                today: 'Today',
                prev: 'Prev',
                next: 'Next'
            },
            customButtons: {
                customToday: {
                    text: 'Today',
                    click: () => {
                        calendarDraggable.today();
                    }
                }   
            },
            editable: true,
            droppable: true,
            events: [
                {
                    id: helper.setID(),
                    title: 'Interview',
                    start: thisMonth + '05T10:30:00',
                    end: thisMonth + '05T13:30:00',
                    className: 'bg-info',
                    location: 'Room 127',
                    description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
                },
                {
                    id: helper.setID(),
                    title: 'Team building trip',
                    start: thisMonth + '15',
                    end: thisMonth + '18',
                    className: 'bg-success',
                    location: 'Florida',
                    description: 'Integer ullamcorper metus sed urna laoreet, sed convallis leo pretium'
                },
                {
                    id: helper.setID(),
                    title: 'Team meeting',
                    start: thisMonth + '25T13:15:00',
                    end: thisMonth + '25T15:00:00',
                    classNames: 'bg-warning',
                    location: 'Conference room',
                    description: 'Maecenas aliquam lectus ut nibh gravida egestas'
                },
                {
                    id: helper.setID(),
                    title: 'Seminarium',
                    start: today,
                    allDay: true,
                    className: 'bg-success',
                    location: 'Hall',
                    description: 'Mauris eu massa ac mauris dapibus consequat a at quam'
                },
                {
                    id: helper.setID(),
                    title: 'Front-End Interview',
                    start: yesterday + 'T09:00:00',
                    end: yesterday + 'T10:15:00',
                    location: 'Room 201',
                    description: 'Ut facilisis odio at lectus ultricies mattis. Morbi a arcu rhoncus ligula lobortis aliquet a in est'
                },
                {
                    id: helper.setID(),
                    title: 'Meeting',
                    start: yesterday + 'T10:30:00',
                    end: yesterday + 'T11:30:00',
                    className: 'bg-success',
                    location: 'Office',
                    description: 'Nunc quis augue non odio porttitor mattis'
                },
                {
                    id: helper.setID(),
                    title: 'Lunch',
                    start: yesterday + 'T12:00:00',
                    end: yesterday + 'T12:40:00',
                    className: 'bg-success',
                    location: 'Diner',
                    description: 'Nam finibus felis hendrerit nibh vestibulum, vitae pellentesque leo sodales'
                },
                {
                    id: helper.setID(),
                    title: 'Scheduled server maintenance',
                    start: thisMonth + '27',
                    end: thisMonth + '29',
                    className: 'bg-danger',
                    description: 'Vestibulum maximus enim hendrerit molestie elementum' 
                }
            ]
        });
        
        calendarDraggable.render();
    })();
}

Advanced (drag & drop, editable, external events)

Drag and drop your event or click in the calendar

Event
Event - Human Resource
Event - Meeting
Event - Important
<div class="row">
    <div class="col-12 col-lg-4 col-xxl-3 mw-lg-300px">

        <button class="btn btn-secondary w-100 mb-6" data-bs-toggle="modal" data-bs-target="#eventModal" id="btnAddEvent">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="14" width="14" class="me-1"><path d="M0,12a1.5,1.5,0,0,0,1.5,1.5h8.75a.25.25,0,0,1,.25.25V22.5a1.5,1.5,0,0,0,3,0V13.75a.25.25,0,0,1,.25-.25H22.5a1.5,1.5,0,0,0,0-3H13.75a.25.25,0,0,1-.25-.25V1.5a1.5,1.5,0,0,0-3,0v8.75a.25.25,0,0,1-.25.25H1.5A1.5,1.5,0,0,0,0,12Z" style="fill: currentColor"/></svg>
            Add Event
        </button>

        <p class="fs-5 mb-4 text-secondary">
            Drag and drop your event or click in the calendar
        </p>

        <div id="draggable">
            <div class='fc-event fc-h-event fc-daygrid-event fc-daygrid-block-event text-bg-success-soft border-0 py-2 px-3 mb-3 cursor-move' data-class="bg-success border-0">
                <span class='fc-event-main text-success'>Event</span>
            </div>
            <div class='fc-event fc-h-event fc-daygrid-event fc-daygrid-block-event text-bg-info-soft border-0 py-2 px-3 mb-3 cursor-move' data-class="bg-info border-0">
                <span class='fc-event-main text-info'>Event - Human Resource</span>
            </div>
            <div class='fc-event fc-h-event fc-daygrid-event fc-daygrid-block-event text-bg-warning-soft border-0 py-2 px-3 mb-3 cursor-move' data-class="bg-warning border-0">
                <span class='fc-event-main text-warning'>Event - Meeting</span>
            </div>
            <div class='fc-event fc-h-event fc-daygrid-event fc-daygrid-block-event text-bg-danger-soft border-0 py-2 px-3 mb-3 cursor-move' data-class="bg-danger border-0">
                <span class='fc-event-main text-danger'>Event - Important</span>
            </div>
        </div>
    </div>

    <div class="col">
        <div id="fullcalendar" class="h-1000px"></div>
    </div>
</div>
// dashly/theme/src/js/vendors/fullcalendar.js
    
const calendarEl = document.getElementById('fullcalendar');

if(calendarEl) {
    (async () => {
        const {Calendar} = await import(/* webpackChunkName: 'fullcalendar.core' */ '@fullcalendar/core');

        const [{default: dayGridPlugin}, {default: timeGridPlugin}, {default: listPlugin}, {default: interactionPlugin, Draggable}, {default: bootstrap5Plugin}, {default: moment}, helper] =
            await Promise.all([
                import(/* webpackChunkName: 'fullcalendar.daygrid' */ '@fullcalendar/daygrid'),
                import(/* webpackChunkName: 'fullcalendar.timegrid' */ '@fullcalendar/timegrid'),
                import(/* webpackChunkName: 'fullcalendar.list' */ '@fullcalendar/list'),
                import(/* webpackChunkName: 'fullcalendar.interaction' */ '@fullcalendar/interaction'),
                import(/* webpackChunkName: 'fullcalendar.bootstrap5' */ '@fullcalendar/bootstrap5'),
                import(/* webpackChunkName: 'moment' */ 'moment'),
                import(/* webpackChunkName: 'helpers' */ '../helpers')
            ]);

        let draggableEl = document.getElementById('draggable'),
            eventModal = document.getElementById('eventModal'),
            eventModalEl = eventModal && new Modal(eventModal),
            eventModalTitle = document.getElementById('eventModalTitle'),
            eventForm = document.getElementById('eventForm'),
            eventName = document.getElementById('eventName'),
            startDate = document.getElementById('startDate'),
            startTime = document.getElementById('startTime'),
            endDate = document.getElementById('endDate'),
            endTime = document.getElementById('endTime'),
            location = document.getElementById('location'),
            description = document.getElementById('description'),
            allDayEvent = document.getElementById('allDayEvent'),
            eventType = document.getElementById('eventType'),
            btnSaveEvent = document.getElementById('btnSaveEvent'),
            btnDeleteEvent = document.getElementById('btnDeleteEvent'),
            btnAddEvent = document.getElementById('btnAddEvent'),
            selectedEvent = null,
            todayDate = moment().startOf('day'),
            yesterday = todayDate.clone().subtract(1, 'day').format('YYYY-MM-DD'),
            today = todayDate.format('YYYY-MM-DD'),
            tomorrow = todayDate.clone().add(1, 'day').format('YYYY-MM-DD'),
            thisMonth = todayDate.format('YYYY-MM'),
            activeId = null;

        let calendar = new Calendar(calendarEl, {
            themeSystem: 'bootstrap5',
            plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin, bootstrap5Plugin],
            initialView: 'dayGridMonth',
            contentHeight: '100%',
            headerToolbar: {
                left: 'prev,next customToday',
                center: 'title',
                right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek'
            },
            buttonText: {
                today: 'Today',
                month: 'Month',
                week: 'Week',
                day: 'Day',
                list: 'List',
                prev: 'Prev',
                next: 'Next'
            },
            customButtons: {
                customToday: {
                    text: 'Today',
                    click: () => {
                        calendar.today();
                    }
                }   
            },
            viewDidMount: () => {
                calendarEl.querySelector('.fc-customToday-button').classList.add('btn-light');
            },
            eventClick: (arg) => {
                eventModal && editEvent(arg);
            },
            slotDuration: '00:15:00',
            slotMinTime: '08:00:00',
            slotMaxTime: '19:00:00',
            editable: true,
            droppable: true,
            dayMaxEvents: true,
            events: [
                {
                    id: helper.setID(),
                    title: 'Interview',
                    start: thisMonth + '05T10:30:00',
                    end: thisMonth + '05T13:30:00',
                    className: 'bg-info',
                    location: 'Room 127',
                    description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'
                },
                {
                    id: helper.setID(),
                    title: 'Team building trip',
                    start: thisMonth + '15',
                    end: thisMonth + '18',
                    className: 'bg-success',
                    location: 'Florida',
                    description: 'Integer ullamcorper metus sed urna laoreet, sed convallis leo pretium'
                },
                {
                    id: helper.setID(),
                    title: 'Team meeting',
                    start: thisMonth + '25T13:15:00',
                    end: thisMonth + '25T15:00:00',
                    classNames: 'bg-warning',
                    location: 'Conference room',
                    description: 'Maecenas aliquam lectus ut nibh gravida egestas'
                },
                {
                    id: helper.setID(),
                    title: 'Seminarium',
                    start: today,
                    allDay: true,
                    className: 'bg-success',
                    location: 'Hall',
                    description: 'Mauris eu massa ac mauris dapibus consequat a at quam'
                },
                {
                    id: helper.setID(),
                    title: 'Front-End Interview',
                    start: yesterday + 'T09:00:00',
                    end: yesterday + 'T10:15:00',
                    location: 'Room 201',
                    description: 'Ut facilisis odio at lectus ultricies mattis. Morbi a arcu rhoncus ligula lobortis aliquet a in est'
                },
                {
                    id: helper.setID(),
                    title: 'Meeting',
                    start: yesterday + 'T10:30:00',
                    end: yesterday + 'T11:30:00',
                    className: 'bg-success',
                    location: 'Office',
                    description: 'Nunc quis augue non odio porttitor mattis'
                },
                {
                    id: helper.setID(),
                    title: 'Lunch',
                    start: yesterday + 'T12:00:00',
                    end: yesterday + 'T12:40:00',
                    className: 'bg-success',
                    location: 'Diner',
                    description: 'Nam finibus felis hendrerit nibh vestibulum, vitae pellentesque leo sodales'
                },
                {
                    id: helper.setID(),
                    title: 'Scheduled server maintenance',
                    start: thisMonth + '27',
                    end: thisMonth + '29',
                    className: 'bg-danger',
                    description: 'Vestibulum maximus enim hendrerit molestie elementum' 
                }
            ]
        });
        
        calendar.render();

        if(draggableEl) {
            new Draggable(draggableEl, {
                itemSelector: '.fc-event',
                eventData: (e) => {
                    return {
                        title: e.innerText,
                        className: e.dataset.class
                    };
                }
            });
        }
        

        let editEvent = (e) => {
            selectedEvent = e.event;
            activeId = selectedEvent ? selectedEvent.id : '';

            eventForm.reset();
            eventForm.classList.remove('was-validated');
            btnDeleteEvent.style.display = 'block';
            eventModalTitle.textContent = 'Edit Event';
            eventModalEl.show();
            eventName.value = selectedEvent.title;
            eventType.value = selectedEvent.classNames[0];
            selectedEvent.extendedProps.location ? location.value = selectedEvent.extendedProps.location : null;
            selectedEvent.extendedProps.description ? description.value = selectedEvent.extendedProps.description : null;
            selectedEvent.start ? startDate.value = moment(selectedEvent.start).format('MM/DD/YYYY') : null;
            selectedEvent.start ? startTime.value = moment(selectedEvent.start).format('HH:mm') : null;
            selectedEvent.end ? endDate.value = moment(selectedEvent.end).format('MM/DD/YYYY') : null;
            selectedEvent.end ? endTime.value = moment(selectedEvent.end).format('HH:mm') : null;
            allDayEvent.checked = selectedEvent.allDay;

            showHideTimes();

         // Retriving flatpickr and tomselect insctances to update values
            let startDateFlatpickr = startDate._flatpickr,
                endDateFlatpickr = endDate._flatpickr,
                startTimeFlatpickr = startTime._flatpickr,
                endTimeFlatpickr = endTime._flatpickr,
                eventTypeSelect = eventType.tomselect; 

            startDateFlatpickr.setDate(startDate.value , true);
            endDateFlatpickr.setDate(endDate.value, true);
            startTimeFlatpickr.setDate(startTime.value , true);
            endTimeFlatpickr.setDate(endTime.value, true);
            eventTypeSelect.sync();
        }

        let newEvent = (e) => {
            eventForm.reset();
            eventForm.classList.remove('was-validated');
            btnDeleteEvent.style.display = 'none';
            eventModalTitle.textContent = 'Add New Event';
            startDate.value = moment().format('MM/DD/YYYY');
            endDate.value = moment().format('MM/DD/YYYY');
            startTime.value = moment().format('HH:mm');
            endTime.value = moment().format('HH:mm');
            activeId = null;

         // Retriving flatpickr and tomselect insctances to update values
            let startDateFlatpickr = startDate._flatpickr,
                endDateFlatpickr = endDate._flatpickr,
                startTimeFlatpickr = startTime._flatpickr,
                endTimeFlatpickr = endTime._flatpickr; 

            startDateFlatpickr.setDate(startDate.value, true);
            endDateFlatpickr.setDate(endDate.value, true);
            startTimeFlatpickr.setDate(startTime.value, true);
            endTimeFlatpickr.setDate(endTime.value, true);
        }

        let createEvent = (e) => {
            eventModalEl.hide();

            calendar.addEvent({
                id: helper.setID(),
                title: eventName.value,
                start: moment(new Date(startDate.value + ' ' + startTime.value)).toDate(),
                end: moment(new Date(endDate.value + ' ' + endTime.value)).toDate(),
                allDay: allDayEvent.checked,
                className: eventType.value || 'bg-success',
                location: location.value,
                description: description.value
            });
            calendar.render();

            eventForm.reset();

            showHideTimes();
        }

        eventModal && allDayEvent.addEventListener('change', (e) => {
            showHideTimes();
        });

        let showHideTimes = (e) => {
            allDayEvent.checked ? startTime.parentNode.style.display = 'none' : startTime.parentNode.style.display = 'block';
            allDayEvent.checked ? endTime.parentNode.style.display = 'none' : endTime.parentNode.style.display = 'block';
        }

        eventModal && btnSaveEvent.addEventListener('click', (e) => {
            e.preventDefault();

            activeId != null && removeEvent(activeId);

            createEvent(e);
        });

        eventModal && btnAddEvent.addEventListener('click', (e) => {
            e.preventDefault();

            newEvent(e);
        });

        eventModal && btnDeleteEvent.addEventListener('click', (e) => {
            e.preventDefault();
            
            activeId != null && removeEvent(activeId);
        });

        function removeEvent(id) {
            calendar.getEventById(id).remove();
        }
    })();
}