Back to Blog
Close-up of vibrant HTML code displayed on a computer screen
Performance
July 10, 2026
8 min read

Why FullCalendar Is Slowing Down Your App (And What to Do About It)

By SimpleCalendarJS Team

SimpleCalendarJS~18 KB gzipped · Zero dependencies · Any framework

Your app's performance dashboard looks fine — until you scroll down to the page with the calendar. FullCalendar is the most popular JavaScript calendar library, with over 19,000 GitHub stars and 1 million+ weekly npm downloads. But popularity doesn't exempt it from shipping a heavy JavaScript payload, and that payload has a measurable cost on your Core Web Vitals.

The bundle tax you didn't budget for

Every FullCalendar setup starts with @fullcalendar/core. A minimal month-view configuration with @fullcalendar/daygrid already costs roughly 43 KB gzipped. Most real-world apps don't stop there:

Package combinationApproximate gzipped size
core + daygrid~43 KB
+ timegrid~55 KB
+ interaction (drag & drop)~65 KB
+ list + @fullcalendar/react~80–100 KB
Full standard bundle (fullcalendar)~100 KB

For context, Google's performance guidance recommends keeping total JavaScript per page under 300 KB gzipped to maintain good Core Web Vitals. A single calendar component consuming a third of that budget is significant — especially on pages where the calendar isn't even the primary feature.

FullCalendar v6 made this worse by embedding Preact as its internal rendering engine. The @fullcalendar/common package saw a 1000%+ size increase on Bundlephobia compared to v5. The plugin architecture means you can tree-shake unused views, but the core cost is fixed and non-negotiable.

Runtime rendering: death by a thousand re-renders

Bundle size is only the first problem. FullCalendar's runtime behaviour creates a second performance cliff once the library is loaded.

GitHub issue #3003 documents a fundamental design problem: dragging a single event triggers eventDestroy and eventAfterRender for every event in the view, not just the one being moved. If your week view has 200 events and a user drags one, FullCalendar destroys and re-renders all 200. Any custom JavaScript attached via event render hooks runs 200 times.

The GitHub issue tracker tells a consistent story:

  • Issue #4395 — Page becomes non-responsive with large event sets. Browsers display "stop this script" warnings.
  • Issue #2524 — Performance regression between v2.0.0 (1,500 events in under a second) and v2.1.0 (same data, 3–4 seconds). removeEventSource reported as "extremely slow."
  • Issue #5673 — Resource timeline view becomes unresponsive with 5,000 rows. Virtual rendering was proposed but milestoned for a future release.
  • Issue #345 — The Angular wrapper triggers long-running script warnings with roughly 100 events in week view.
  • Issue #5304 — RRule expansion freezes the calendar on year views with hundreds of recurring event definitions.
  • Issue #4507 — Drag and resize interactions become sluggish proportionally to total event count.

These aren't edge cases from 2018. Many remain open or were only partially addressed in subsequent releases.

What this costs you in Core Web Vitals

Google ranks pages using three Core Web Vitals metrics. FullCalendar's performance profile directly affects two of them:

Largest Contentful Paint (LCP) measures when the largest visible element finishes rendering. The threshold for a "good" score is under 2.5 seconds. FullCalendar's JavaScript must be downloaded, parsed, and executed before the calendar paints — and on mobile connections, 80–100 KB of gzipped JavaScript takes meaningful time to transfer and process. Every millisecond spent parsing calendar code is a millisecond subtracted from your LCP budget.

Interaction to Next Paint (INP) replaced First Input Delay in 2024 as the responsiveness metric. INP measures the latency of all user interactions throughout the page lifecycle, not just the first one. The threshold for "good" is under 200ms. When a user clicks a date or drags an event and FullCalendar re-renders the entire view in response, that processing time contributes directly to INP. With 200+ events, a single drag can block the main thread for hundreds of milliseconds.

The combination is punishing: a heavy bundle degrades LCP on load, and expensive re-renders degrade INP during use.

Measuring the damage in your app

Before making changes, quantify the problem. Run a Lighthouse audit on the page containing your FullCalendar instance and note:

  1. Total Blocking Time (TBT) — proxy for INP in lab testing. Look for FullCalendar-related scripts in the "Reduce JavaScript execution time" audit.
  2. JavaScript bundle size — use your bundler's analyzer (webpack-bundle-analyzer, rollup-plugin-visualizer, or @next/bundle-analyzer) to see exactly how many KB FullCalendar contributes.
  3. Main thread activity — open Chrome DevTools Performance tab, interact with the calendar, and look for long tasks (yellow bars exceeding 50ms).

If FullCalendar is consuming more than 20% of your total JavaScript budget and your calendar doesn't require resource timelines or complex RRULE recurrence, you're over-paying for what you're using.

The fix: drop the weight

SimpleCalendarJS ships at ~14 KB gzipped with zero dependencies. That's roughly one-third the size of FullCalendar's minimum setup — and it includes month, week, and day views, event fetching, click handlers, drag-and-drop, and event resizing out of the box.

import SimpleCalendarJs from 'simple-calendar-js'; import 'simple-calendar-js/simple-calendar-js.css'; const calendar = new SimpleCalendarJs('#calendar', { fetchEvents: async (start, end) => { const res = await fetch(`/api/events?start=${start.toISOString()}&end=${end.toISOString()}`); return res.json(); }, onEventClick: (event) => openEventModal(event), enableDragAndDrop: true, enableResize: true, });

No plugins to install. No framework adapters. No Preact hidden inside the bundle. The same code works in React, Vue, Svelte, Angular, Astro, or a plain HTML page.

Performance comparison

MetricFullCalendar (typical setup)SimpleCalendarJS
Bundle size (gzipped)43–100+ KB~14 KB
DependenciesPreact (internal), 3–6 npm packagesZero
Re-render on event dragAll events in viewAffected event only
Framework adapters neededYes (@fullcalendar/react, etc.)No — universal API
Virtual renderingNot yet (milestoned future)Lightweight DOM
PricingFree (standard) / $480+ per dev (premium)Free personal / license req. commercial

Migrating: what changes, what stays

If your FullCalendar usage is limited to displaying events and handling clicks (the majority of implementations), migration is straightforward:

// Before: FullCalendar import { Calendar } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import interactionPlugin from '@fullcalendar/interaction'; const calendar = new Calendar(document.getElementById('calendar'), { plugins: [dayGridPlugin, interactionPlugin], events: '/api/events', eventClick: (info) => handleClick(info.event), }); calendar.render(); // After: SimpleCalendarJS import SimpleCalendarJs from 'simple-calendar-js'; import 'simple-calendar-js/simple-calendar-js.css'; const calendar = new SimpleCalendarJs('#calendar', { fetchEvents: async (start, end) => { const res = await fetch(`/api/events?start=${start.toISOString()}&end=${end.toISOString()}`); return res.json(); }, onEventClick: (event) => handleClick(event), });

The API surface is deliberately simpler. You pass a fetchEvents function that returns an array of event objects, and SimpleCalendarJS handles the rest — view rendering, navigation, locale formatting, and CSS theming via variables.

When to stay with FullCalendar

FullCalendar is the right tool when your requirements include:

  • Resource scheduling — named resources on a horizontal timeline (room booking, staff rosters)
  • RRULE recurrence — complex recurring event patterns with exceptions
  • Enterprise timeline views — multi-week, multi-resource drag-and-resize workflows
  • iCalendar feed ingestion — direct parsing of .ics files from external calendars

If none of those describe your use case, you're paying a performance tax for features you don't use.

Summary

  • FullCalendar ships 43–100+ KB gzipped depending on plugins — a significant fraction of your JavaScript budget on any page.
  • FullCalendar v6's internal Preact dependency caused a 1000%+ bundle increase for the common package versus v5.
  • Runtime re-rendering is not scoped — dragging one event can trigger a full-view re-render, degrading INP with large event sets.
  • Multiple open GitHub issues document performance degradation at 100–5,000 events across different views and framework wrappers.
  • SimpleCalendarJS delivers month, week, and day views at ~14 KB gzipped with zero dependencies, scoped re-renders, and no framework lock-in.

Sources & Further Reading

Research & References

Image Credits

All images free to use under the Pexels License.

Frequently Asked Questions

Why is FullCalendar so slow?

FullCalendar's performance issues stem from three factors: a large JavaScript bundle (43–80+ KB gzipped depending on plugins), a rendering model that re-renders every event in the view on any change (GitHub issue #3003), and the lack of virtual rendering for large datasets (issue #5673). On mobile devices, this combination can push Interaction to Next Paint (INP) well above Google's 200ms threshold.

How big is FullCalendar's bundle size?

A minimal FullCalendar setup with just @fullcalendar/core and @fullcalendar/daygrid ships approximately 43 KB gzipped. Adding timegrid, interaction, list, and a framework adapter pushes the total to 80–100+ KB gzipped. FullCalendar v6 also bundles Preact internally, which contributed to a significant size increase from v5 (documented in GitHub issue #7029).

Does FullCalendar affect Core Web Vitals?

Yes. FullCalendar's JavaScript payload increases both Largest Contentful Paint (LCP) and Interaction to Next Paint (INP). The browser must download, parse, and execute the bundle before the calendar renders, directly delaying LCP. Event interactions that trigger full view re-renders contribute to poor INP scores, especially with hundreds of events.

How many events can FullCalendar handle before it slows down?

Performance degradation becomes noticeable at around 100–500 events depending on the view type. GitHub issue #4395 documents the page becoming unresponsive with large event sets. The Angular wrapper (issue #345) shows severe slowdowns with about 100 events in week view. Resource timeline view (issue #5673) becomes unusable with 5,000+ resources.

What is the best lightweight alternative to FullCalendar?

SimpleCalendarJS weighs ~14 KB gzipped with zero dependencies — roughly one-third the size of a minimal FullCalendar setup. It supports month, week, and day views, event fetching, click handlers, drag-and-drop, event resizing, and 34+ locales in a single package. It's free for personal and open-source use, with commercial licenses available at $49/year or $199 lifetime per project.

Can I lazy-load FullCalendar to improve performance?

Yes, dynamic imports (React.lazy, import()) can defer FullCalendar's bundle until the calendar is needed. This helps LCP on pages where the calendar isn't above the fold. However, lazy loading doesn't solve FullCalendar's runtime rendering performance — once loaded, interaction slowdowns with large event sets remain.

Add a calendar to your app today

Free for personal projects. $49/year or $199 lifetime per commercial project.