26 October 2026
3.3 is the only minor before 4.0. Each new option's default reproduces 3.2.1, and docs/migration-v4.md lists what 4.0 will remove or change. One intentional exception, by the translations contract: a full translations object should add the nine new keys listed under Fixes. Until it does they show in English, not as raw key names.
Features
- feat: events can be resized by dragging their end edge, or the start edge with
eventResizableFromStart. Time grids snap to the slot and day grids to whole days; a resize stays in its grid and keeps the resource. A resize is oneonEventsChangeaction, reported with the newendDelta. NeweventDurationEditableand per-eventdurationEditable(#345) — Closes#93 - feat: new
editable,eventStartEditableandeventAllow(dropInfo, draggedEvent)props, and per-eventeditable/startEditable, decide what can be moved. An event's own flag beats the calendar's, and a specific flag beatseditable, as FullCalendar resolves them.eventAllowis asked on every placement while dragging and again at drop; a refused placement draws no preview.disableDragAndDropstill works and is now an alias ofeditable={false}(#340) - feat:
onEventsChangereportsdelta({ days, milliseconds }: whole calendar days plus the change in time of day) and arevert()that undoes the whole action in the calendar's own store, recurring scopes included, without firing callbacks. There is nooldEvent:EventsChange.eventis already the event before the change (#342) - feat: new
slotMinTimeandslotMaxTime('HH:mm', end exclusive, up to'24:00') set the visible hours of the day, week and resource hour grids, independently ofhideNonBusinessHours(#346) - feat: new
snapDuration(15or30) lets drag, resize and drag-to-create snap finer than the slot, in the vertical grids and the horizontal resource timeline, soslotDuration={60}withsnapDuration={15}gives quarter-hour placement.slotDurationstays15,30or60(#349,#356) — Closes#101,#138 - feat: new
slotHeaderFormat,eventTimeFormat,dayHeaderFormatandtitleFormat(Intl.DateTimeFormatOptions, rendered in the calendar's locale and zone) anddisplayEventTime/displayEventEndcontrol how times and dates are written. Unset options render exactly as before (#343) - feat: new
allDaySlot,nowIndicator,weekNumbers(also on month rows) andweekNumberCalculation('local'or'ISO').scrollTimeandscrollToNownow scroll to the exact minute instead of the hour (#344) - feat:
hiddenDaysnow applies to the month, week, day and every resource view, and the newweekends={false}hides Saturday and Sunday. Day view navigation skips hidden days; bars over a hidden day are clamped to the days drawn (#347) - feat:
eventsaccepts an async function(info, { signal }) => Promise<Event[]>called with the visible range, withloading,lazyFetching,onEventsErrorandrefetchEvents(). A superseded fetch is aborted (#348) - feat:
resourcesaccepts an async function too, withresourcesSet,onResourcesError,refetchResources()andrefetchResourcesOnNavigate.loadingcovers events and resources together (#353) - feat:
IlamyCalendartakes arefwhosecurrentis the same API asuseIlamyCalendarContext(), plusscrollToTime(time), so a toolbar or sidebar of your own can drive the calendar (#351)
Fixes
- fix: in the vertical resource week (hourly and daily), the week number now fades in on navigation like the day labels beside it and the week view's number; it used to change without animating (
#380) - fix: with a
localewhose week starts on another day than Sunday (fr, de, ar) and the defaultfirstDayOfWeek, the week view showed the previous week on a Sunday, the month grid started a week early so the month's last days were never drawn, and the event form's date picker offered no 30th or 31st.firstDayOfWeeknow alone decides where a week starts, whatever the locale (#374) - fix: dragging a recurring occurrence to another weekday or day of the month and choosing "This and following events" now continues the series on that day; before, the new series kept the old weekday or date and the moved occurrence disappeared (
#377) - fix: event bars in the time grids sat 4 to 7px below their time slot, because the bar's wrapper sat on the text baseline (
#345) - fix: in the horizontal resource timeline, an event outside the visible hours was stretched to the edge column or drawn past the grid instead of being left out, and week-mode columns were measured from the first column. Events are now cut to the columns they overlap, end exclusive. Reachable before with
hideNonBusinessHours(#346) - fix: on a daylight-saving change day, in a calendar zone or on a computer whose clocks change, hour rows could be an hour off, so events landed a row away and a drop did not land on the cell it was dropped on. Rows are now built from wall-clock times in the calendar's zone (
#346) - fix: in a multi-day horizontal resource timeline,
scrollTimeandscrollToTimefor the last visible hour landed at the end of the range instead of the first day (#351) - fix: dropping an event where it already was no longer fires
onEventUpdateoronEventsChange, and no longer opens the recurring scope dialog (#342) - fix:
deltawas an hour short for a drop into the hour skipped by the spring clock change (#342) - fix: with a
snapDurationfiner than the slot, the drag preview stayed on the slice where the pointer entered a cell while the drop used the pointer's final position; the preview now follows it (#352) - fix: an all-day event dropped on a time slot now starts at that slot, wherever its bar was grabbed, and lasts one hour; a timed event dropped on the all-day row now ends at the next midnight. Before, the start depended on the grab point and the end kept the old length (FullCalendar's
defaultTimedEventDuration/defaultAllDayEventDuration) (#363) - fix: moving an event across a clock change keeps its wall-clock length, so it no longer grows or shrinks by an hour,
deltaequalsendDelta, and the drag preview's end time matches what is saved (#363) - fix: around a daylight-saving change, week, month and year days, the day view's all-day cell, form times, next/previous and the date picker no longer produce an instant an hour off. Re-saving an unchanged one-day all-day event on a spring-forward day no longer stores a zero-length event, and a picked time no longer saves an hour late (
#368) - fix: the event form no longer saves an end before its start, and ticking then unticking All day no longer rewrites the end time to 23:59 (
#368) - fix: a fetch still in flight when
eventschanges from a function to an array no longer overwrites the array; a failed firstresourcesfetch is no longer retried by plain navigation (onlyrefetchResources(), orrefetchResourcesOnNavigate) (#364) - fix: iCal export folds lines longer than 75 octets, ends with CRLF, and writes all-day
UNTIL,EXDATEandRECURRENCE-IDas DATE values (RFC 5545 3.1, 3.4, 3.3.10, 3.8.5.1, 3.8.4.4) (#365) - fix: the week view's week number now numbers the week drawn, not the current date, so it matches the month row;
titleFormatin the month and year views names the whole month or year; the agenda lists a timed multi-day or overnight event under every day it covers (#366) - fix: in the vertical resource month and daily resource week, two events in one room on the same day no longer cover each other; drag-to-create keeps numeric resource ids, follows the pointer over existing events and is cancelled cleanly by Escape; renaming a multi-resource event no longer adds a
resourceId(#367) - fix: Enter and Space open a focused event, events that cannot move are still reachable by keyboard, bars no longer announce a keyboard drag that does not exist, and the form's date and time pickers and the Repeat checkbox are named by their labels (
#369) - fix: recurring "this and following" edits and deletes never leave COUNT and UNTIL together, the new series keeps only the remaining COUNT, a moved occurrence is cut correctly, cutting at the first occurrence replaces the series, unticking Repeat stops the series, and deleted occurrences stay deleted after a time change. "All events" keeps the series' weekday and now reports only the time change it stored (
#370) - fix: the date and time pickers' labels, placeholder and empty state, and the month and year navigation labels of the date pickers, are now translated instead of hard-coded English. Nine new
Translationskeys:selectTime,noTimeFound,pickDate,previousMonth,nextMonth,previousYear,nextYear,previousYears,nextYears. A fulltranslationsobject should add them (docs/translation-usage.md); a key it lacks shows in English until it does (#369,#383) - fix:
weekNumberCalculation: 'local'showed week numbers a week early in a London, Lisbon or Dublin calendar (and some machine zones) from April to October. Week numbers are now counted from the calendar day alone (#375) - fix: with no
timezoneprop in a browser in London, Dublin or Lisbon, an all-day event created on 30 March 2025 ended an hour late, and after next and previous in the month view the cells from 31 March on started an hour late (#378) - fix: the title dropdown's month and year pickers kept the UTC offset of the date they started from, so picking June from a January date was an hour off in a
timezonewith daylight saving, and a date just after midnight picked into January landed on 31 December (#376) - fix: the event form saved an event whose end equalled its start (nothing draws it) when an End Time before the start or a Start Time after the end was picked. The other time now follows, so the event keeps the length it opened with (RFC 5545 section 3.8.2.2) (
#376) - fix: saving an existing event with its schedule untouched, or with only its title changed, no longer rewrites its
startandend: an all-day event stored as UTC midnights grew from 24 to 48 hours in a New York calendar (#376) - fix: the title of an agenda with a Week window named only the days
hiddenDaysleft in, while the list showed the whole week; the title now matches the list (#376) - fix: closing the event form, opened from an event bar or from a "+N more" list, now returns keyboard focus to the element that opened it instead of the page body (WCAG 2.4.3) (
#376) - fix: in the resource day and week views, dragging a timed event onto the all-day strip after turning the wheel during the drag dropped nothing (or on a time slot); the strip is now hit where it is drawn. Present in 3.2.1 (
#381)
Performance
- perf: a day's hour rows are derived once and built once per render, not once per resource. Building the columns of a 500-resource week went from 54 ms (UTC) and 203 ms (New York zone) on 3.2.1 to 4.1 ms and 6.6 ms, measured with
packages/calendar/scripts/bench-day-hours.tsx(#354) - perf: a visible range's events are processed once per render and bucketed by resource and column in one pass, instead of once per resource row, and recurrence overrides are grouped by series once. 500 resource rows with 5,200 events in 200 series: 1,516 ms to 4.7 ms in a benchmark (
#357) - perf: events are placed by binary search over column start times measured once per layout, with no date object per event. A 100-row resource month in the New York zone lays out in 137 ms, down from 1,051 ms on 3.2.1 (
#358) - perf: measured in Chromium on a 2,000-event month against 3.2.1, at the 90th percentile: next/previous 310 to 43 ms, opening the event form 328 to 52 ms, dropping an event 670 to 127 ms, and first render 464 to 195 ms (median). A recurring month drops from 911 to 72 ms. Dragging stays under 10 ms of script per pointer step (
#357,#358) - perf: an event bar is one drag source for moving and for resizing either edge; a press on a resize handle resizes it (
#359). A 500-event week is close to 3.2.1 rather than faster (next/previous 46 to 53 ms at the 90th percentile), because every resizable bar now draws its resize handles - perf: a column of the vertical grids is one drop target instead of one per slot, which dnd-kit registers in time quadratic in their number. A resource week of 30 rooms mounts in 0.84 s at 15-minute slots (20,160 slots), down from 6.2 s on 3.2.1, and 0.58 s at 30 minutes, down from 1.83 s. Dragging in it costs a crossing step at most 37 ms of script instead of 221, and dropping 191 ms instead of 691 (Chromium, median of 3) (
#379,#381)
Docs
- docs: the README and website no longer claim resize or overlap prevention (neither existed in 3.2.1), "100% test coverage" or "100+ locales", and say what the
timezone, size and dark-mode claims actually cover (#337) - docs:
docs/migration-v4.mdlists every name 4.0 removes and the defaults that may change;disableDragAndDropandIlamyResourceCalendarcarry a@deprecatedtag.docs/roadmap.mdis the plan through 4.3 (#361,#372)
Internal
- chore: the core bundle (
dist/index.jsplus the chunks it imports) grows about 26.3% gzipped over 3.2.1, 42.1 KB to 53.2 KB, for the features and performance work above - chore: the e2e harness takes its port from
E2E_PORTso several worktrees can run it at once (#336) - chore:
apps/perf, a runtime benchmark that measured the performance work above against 3.2.1 (#362) - test: the engine tests no longer depend on the machine's time zone (
#341);docs/timezones.mdexplains why the suite runs in UTC (#350)