Drag & Drop
trud-calendar uses the Pointer Events API for all drag interactions — working seamlessly on mouse, touch screens, and stylus devices with zero extra dependencies.
Enable drag and drop
Section titled “Enable drag and drop”Set enableDnD={true} and provide handlers:
<Calendar events={events} enableDnD onEventDrop={(event, newStart, newEnd) => { await api.updateEvent(event.id, { start: newStart, end: newEnd }); }} onEventResize={(event, newStart, newEnd) => { await api.updateEvent(event.id, { start: newStart, end: newEnd }); }} onSlotSelect={(start, end) => { // User dragged across empty slots to select a time range openCreateModal({ start, end }); }}/>Interactions
Section titled “Interactions”Event move
Section titled “Event move”- Month view: Drag an event pill from one day cell to another. The date changes but the original time is preserved.
- Week/Day view: Drag an event from one time slot to another. Both date and time change, snapping to 15-minute increments.
- Event duration is always preserved.
Event resize
Section titled “Event resize”- In Week/Day view, drag the bottom edge of an event to change its duration.
- Snaps to 15-minute increments.
- Fires
onEventResizewith the updated start/end times.
Drag-to-create (slot selection)
Section titled “Drag-to-create (slot selection)”- Drag across empty time slots in Week/Day view to select a time range.
- Fires
onSlotSelect(start, end)with the selected range. - Great for “create event” flows — the user drags to select when, then fills in the details.
Touch support
Section titled “Touch support”All interactions work on touch devices out of the box:
- Touch and hold an event, then drag to move
- Touch the resize handle at the bottom of an event and drag to resize
- Touch and drag across empty slots to select a time range
The calendar uses touch-action: none on interactive elements to prevent the browser from scrolling during drag operations. A 5px movement threshold distinguishes taps from drags.
Callbacks
Section titled “Callbacks”onEventDrop
Section titled “onEventDrop”onEventDrop?: ( event: CalendarEvent, // The original event object newStart: DateTimeString, // "2026-03-15T10:00:00" newEnd: DateTimeString, // "2026-03-15T11:00:00") => void;onEventResize
Section titled “onEventResize”onEventResize?: ( event: CalendarEvent, // The original event object newStart: DateTimeString, // "2026-03-15T10:00:00" newEnd: DateTimeString, // "2026-03-15T11:30:00" (new duration)) => void;onSlotSelect
Section titled “onSlotSelect”onSlotSelect?: ( start: DateTimeString, // "2026-03-15T10:00:00" end: DateTimeString, // "2026-03-15T11:00:00") => void;Example: Full interaction handler
Section titled “Example: Full interaction handler”function App() { const [events, setEvents] = useState(initialEvents); const [creating, setCreating] = useState(null);
const handleDrop = async (event, newStart, newEnd) => { // Optimistic update setEvents((prev) => prev.map((e) => e.id === event.id ? { ...e, start: newStart, end: newEnd } : e ) ); await fetch(`/api/events/${event.id}`, { method: "PATCH", body: JSON.stringify({ start: newStart, end: newEnd }), }); };
return ( <Calendar events={events} enableDnD onEventDrop={handleDrop} onEventResize={handleDrop} onSlotSelect={(start, end) => setCreating({ start, end })} /> );}