Calendarkit Pro V2
Features

Drag & Drop

Move and resize events with fluid animations powered by @dnd-kit.

Drag & Drop

CalendarKit Pro uses @dnd-kit for drag-and-drop. Events can be moved to new times/dates and resized by dragging their bottom edge — all with smooth animations.

Enable drag & drop

Drag & drop is enabled by default. Use the onEventDrop callback to persist changes:

<ProScheduler
  events={events}
  onEventDrop={(event, newStart, newEnd) => {
    setEvents((prev) =>
      prev.map((e) =>
        e.id === event.id ? { ...e, start: newStart, end: newEnd } : e
      )
    );
  }}
  ...
/>

Enable event resize

Use onEventResize to persist resize changes:

<ProScheduler
  events={events}
  onEventResize={(event, newStart, newEnd) => {
    setEvents((prev) =>
      prev.map((e) =>
        e.id === event.id ? { ...e, start: newStart, end: newEnd } : e
      )
    );
  }}
  ...
/>

Disable drag & drop

Set readOnly to disable all drag-and-drop and resize interactions:

<ProScheduler readOnly events={events} ... />

Snap grid

Events snap to a 15-minute grid during drag and resize. This corresponds to 15px of movement (since the time grid uses 60px per hour for Week view and 80px per hour for Day view).

Drag overlay

While dragging, a stylized "ghost" overlay is shown that mirrors the event's color and size. It rotates slightly and scales up to give clear visual feedback.

Cross-resource dragging

In the Resource view, you can drag events horizontally between resource columns. The onEventDrop callback receives the updated start/end and you should also update the resourceId:

<ProScheduler
  view="resource"
  resources={resources}
  events={events}
  onEventDrop={(event, newStart, newEnd) => {
    // Note: resource view encodes the target resource in the drop position.
    // Update both times and handle resourceId via onEventUpdate if needed.
    setEvents((prev) =>
      prev.map((e) =>
        e.id === event.id ? { ...e, start: newStart, end: newEnd } : e
      )
    );
  }}
  ...
/>

Full example

The live demo below shows drag-and-drop, resize, event creation/update/delete, view switching (Week / Day / Resource), and cross-resource dragging — all interactive.

Source code

'use client';

import { useState } from 'react';
import { ProScheduler } from 'calendarkit-prov2';
import type { CalendarEvent, ViewType, Resource } from 'calendarkit-prov2';
import 'calendarkit-prov2/calendarkit.css';
import { addHours, addDays, startOfWeek, format } from 'date-fns';

const weekStart = startOfWeek(new Date());

const initialEvents: CalendarEvent[] = [
  { id: '1', title: 'Team Standup (drag me)',  start: addHours(addDays(weekStart, 1), 9),  end: addHours(addDays(weekStart, 1), 9.5),  color: '#3b82f6', resourceId: 'room-a' },
  { id: '2', title: 'Workshop (resize me)',    start: addHours(addDays(weekStart, 2), 10), end: addHours(addDays(weekStart, 2), 12),   color: '#8b5cf6', resourceId: 'room-b' },
  { id: '3', title: 'Lunch Break',             start: addHours(addDays(weekStart, 3), 12), end: addHours(addDays(weekStart, 3), 13),   color: '#f59e0b', resourceId: 'room-a' },
  { id: '4', title: 'Design Review',           start: addHours(addDays(weekStart, 4), 14), end: addHours(addDays(weekStart, 4), 15.5), color: '#10b981', resourceId: 'room-c' },
];

const resources: Resource[] = [
  { id: 'room-a', label: 'Conference Room A', color: '#3b82f6' },
  { id: 'room-b', label: 'Conference Room B', color: '#8b5cf6' },
  { id: 'room-c', label: 'Lounge',            color: '#10b981' },
];

const fmt = (d: Date) => format(d, 'EEE dd MMM, HH:mm');

export function DragDropExample() {
  const [events, setEvents] = useState<CalendarEvent[]>(initialEvents);
  const [view,   setView]   = useState<ViewType>('week');
  const [date,   setDate]   = useState(new Date());
  const [log,    setLog]    = useState('Drag, resize, or create an event to see activity here.');

  const patchEvent = (id: string, start: Date, end: Date, extra?: Partial<CalendarEvent>) =>
    setEvents((prev) => prev.map((e) => (e.id === id ? { ...e, start, end, ...extra } : e)));

  return (
    <div className="flex flex-col gap-3 not-prose">
      {/* Activity log */}
      <div className="rounded-lg border border-fd-border bg-fd-muted px-4 py-2 text-xs text-fd-muted-foreground font-mono min-h-[36px] flex items-center gap-2">
        <span className="w-1.5 h-1.5 rounded-full bg-green-500 shrink-0" />
        {log}
      </div>

      {/* View switcher */}
      <div className="flex gap-2">
        {(['week', 'day', 'resource'] as ViewType[]).map((v) => (
          <button key={v} onClick={() => setView(v)}
            className={`px-3 py-1 rounded-md text-xs font-medium border transition-colors ${
              view === v ? 'bg-fd-primary text-fd-primary-foreground border-fd-primary' : 'border-fd-border hover:bg-fd-muted'
            }`}>
            {v.charAt(0).toUpperCase() + v.slice(1)}
          </button>
        ))}
      </div>

      <div className="h-[680px] border border-fd-border rounded-xl overflow-hidden">
        <ProScheduler
          view={view} onViewChange={setView}
          events={events} date={date} onDateChange={setDate}
          resources={resources} initialScrollHour={8}
          onEventDrop={(event, start, end) => {
            patchEvent(event.id, start, end);
            setLog(`Moved "${event.title}" → ${fmt(start)} – ${fmt(end)}`);
          }}
          onEventResize={(event, start, end) => {
            patchEvent(event.id, start, end);
            setLog(`Resized "${event.title}" → ends ${fmt(end)}`);
          }}
          onEventCreate={(e) => {
            const created = { ...e, id: crypto.randomUUID(), title: e.title ?? 'New Event' } as CalendarEvent;
            setEvents((prev) => [...prev, created]);
            setLog(`Created "${created.title}" at ${fmt(created.start)}`);
          }}
          onEventUpdate={(updated) => {
            setEvents((prev) => prev.map((e) => (e.id === updated.id ? updated : e)));
            setLog(`Updated "${updated.title}"`);
          }}
          onEventDelete={(id) => {
            const target = events.find((e) => e.id === id);
            setEvents((prev) => prev.filter((e) => e.id !== id));
            setLog(`Deleted "${target?.title ?? id}"`);
          }}
        />
      </div>
    </div>
  );
}

On this page