12 September 2026
v3 aligns an event's end with RFC 5545 and the Google Calendar API: it is now exclusive. In practice this affects one shape of event — an end at exactly midnight — and most calendars upgrade without changes. Existing all-day events render exactly as they did. The release also brings a renderEvent that knows which slice of an event it is drawing, server-rendering that hydrates cleanly, and a run of timezone fixes.
⚠️ Breaking changes
v3 migration guide — it lists exactly who is affected. If nothing in your data ends at exactly midnight, there is nothing to do.
endis exclusive. An event runs up to, but not including, itsend, so only anendat exactly midnight changes: an event from2025-01-13T00:00to2025-01-14T00:00covers 13 January only, where it used to paint 14 January as well. Anendduring the day, such as2025-01-14T23:59, still covers that day, and timed events inside a day are unaffected. To keep a bar that reaches a given day, end it during that day or at the following midnight. This matches RFC 5545 §3.6.1 and the Google Calendar API (#250) — Closes#248- All-day events store an exclusive end, and display identically. A new all-day event is stored as the midnight after the last covered day rather than
23:59. Both forms cover the same days, so existing events render exactly as before and nothing needs migrating; the form still shows you the last covered day. This is visible only if you readevent.endyourself or compare an iCal export, where a one-day event is nowDTSTART;VALUE=DATE:20250804/DTEND;VALUE=DATE:20250805, as the RFC requires (#250) CellInfo.endis the next midnight. WhatonCellClickreceives (and thedata-endattribute plugins read) ends on the next boundary rather than at23:59, matching the hour and 15-minute cells, which already did (#250)
Features
- feat:
renderEventreceives a second argument saying which slice of the event the current bar draws. An event longer than its row is drawn as one bar per row, and until now every call looked identical, so a custom renderer could not square a cut edge and round a real one.segment.isStart/segment.isEndfollow FullCalendar's event render hook (#264) — Closes#263— Thanks @habovh!
Fixes
- fix: a date carrying an explicit offset (
Zor±HH:MM) keeps the instant it names instead of being re-read as wall-clock time in the configured zone, and thetimezoneprop now applies on mount rather than only when it changes (#249) — Closes#247 - fix:
initialDateopens on the date you give it. A string without an offset is anchored in the calendar's zone, so'2025-03-10'opens on 10 March wherever it is read; aDate, aDayjsor an offset-carrying string keeps its instant and only the clock it renders against changes (#266) — Closes#265— Thanks @habovh! - fix: server-rendered calendars hydrate without a mismatch. The drag-and-drop context now takes a stable id from React's
useIdinstead of a module-level counter that carried between requests (#262) — Closes#261— Thanks @habovh! - fix: week and day views no longer disable days belonging to the neighbouring month, so the week of 31 March is interactive on both sides. The month grid keeps greying its own padding (
#260) — Closes#259— Thanks @habovh! - fix:
slotDurationreaches the droppable cells, so a cell click on a 15- or 30-minute grid reports the slot you configured rather than a full hour (#256) — Closes#255— Thanks @johanjq! - fix: a recurrence override that has been moved no longer renders twice, and an event dragged onto another resource actually moves there (
#257) - fix: header labels animate only when their text changes. Navigating a month used to replay the fade over an unchanged weekday row; now a week column keeps its weekday and animates its number, and a month column does the reverse (
#273)
Performance
- perf: date comparisons answer by instant rather than by formatting, and the year view resolves its events in one pass instead of one query per day (
#253) — Closes#245— Thanks @mattanderson-io!
Internal
- test: a deterministic browser (E2E) suite covering all 12 rendering surfaces, with the clock, zone, locale and viewport pinned so a calendar test cannot depend on the day it runs (
#275) - chore: the docs site moved to Astro 7.2.10 for a critical AVIF decode advisory. The published package is unaffected (
#276) — Closes#274