Drag, Drop & Selection

Two flags unlock the interactive behaviors: editable lets users move and resize events, selectable lets them drag across empty space to pick a range. Sensible popover UIs are built in — callbacks let you replace them.

Built-in popovers (zero config)

With no callbacks configured, the calendar behaves like Google Calendar out of the box: clicking an event opens a details popover (time, calendar, location, description, delete); clicking or drag-selecting empty space opens a quick-create popover with a title input and calendar picker.

The quick-create popover lets you fine-tune the picked range before saving: start and end are editable date/time inputs (free down to the minute — no snapping to slotDuration), with an “all day” toggle and multi-day ranges supported.

Try it: click events, drag empty slots

Sep 6 – 12, 2026

Sun
6
Mon
7
Tue
8
Wed
9
Thu
10
Fri
11
Sat
12
All-day
1 AM
2 AM
3 AM
4 AM
5 AM
6 AM
7 AM
8 AM
9 AM
10 AM
11 AM
12 PM
1 PM
2 PM
3 PM
4 PM
5 PM
6 PM
7 PM
8 PM
9 PM
10 PM
11 PM
12 AM
Precedence — providing onEventClick suppresses the details popover; providing onSelect / onDateClick suppresses quick-create. Set eventDetails={false} or quickCreate={false} to turn the built-ins off without supplying handlers. Quick-create activates when the calendar is selectable or editable.

Custom handling via callbacks

Editable + selectable, custom callbacks

Sep 6 – 12, 2026

Sun
6
Mon
7
Tue
8
Wed
9
Thu
10
Fri
11
Sat
12
All-day
1 AM
2 AM
3 AM
4 AM
5 AM
6 AM
7 AM
8 AM
9 AM
10 AM
11 AM
12 PM
1 PM
2 PM
3 PM
4 PM
5 PM
6 PM
7 PM
8 PM
9 PM
10 PM
11 PM
12 AM
· drag events, resize them, or drag across empty slots ·

What users can do

  • Drag an event block to another time or day (week/day views).
  • Drag an event pill to another day (month view), or an all-day bar along the all-day lane.
  • Drag the bottom edge of a block to resize it.
  • Drag a recurring instance — a confirm popover offers “this event” or “this and following”.
  • Drag across empty slots to select a time range (fires onSelect).
  • Drag across month cells to select a day range.
  • Click an empty slot or day (fires onDateClick).
Touch devices — tap works like click; long-press (~300 ms, with a haptic tick) then drag to move, resize or select — a plain swipe scrolls as usual.

Callbacks

CallbackFires whenPayload
onEventClickan event is clicked or activated by keyboard(instance, mouseOrKeyboardEvent)
onDateClickan empty slot / day cell is clicked(date, allDay)
onSelecta drag-selection finishes{ start, end, allDay }
onEventChangea drag or resize lands{ event, oldStart, oldEnd, start, end, revert }
onEventCreatethe quick-create popover adds an event(event)
onEventDeletean event (or one occurrence of a series) is deleted(event, occurrence?)
onSeriesDetacha dragged/resized occurrence is split out of its series{ series, detached, occurrence }
onRangeChangethe visible date range changes — fetch events here(start, end)
onViewChange / onDateChangethe view / focused date changes(view) / (date)

Persisting to a server, with rollback

The calendar applies the change optimistically, then calls onEventChange. If your API call fails, call revert() to snap the event back:

async function onEventChange({ event, start, end, revert }) {
  try {
    await api.updateEvent(event.id, { start, end });
  } catch {
    revert(); // restore the previous times
  }
}

Permission rules

  • editable on the calendar is the master switch.
  • source.editable = false locks all events of that calendar.
  • event.editable overrides both, per event.
  • Instances of recurring events are never draggable — a recurring series has no single “time” to move. Change the series’ start/recurrence in your data instead.