@widgetkit/scheduler-react@widgetkit/scheduler-vue@widgetkit/scheduler-js

Scheduler

A time-based multi-resource scheduler with drag-and-drop, resize, snapping, collision detection, and custom rendering. Zero external dependencies.

Introduction

TimelineScheduler renders resources as rows and events as positioned blocks on a time axis. It handles all pointer interactions internally — drag-and-drop, resize, click-to-create — and fires callbacks so your app stays in control of the data. Toggle the options below to explore all props.

Interaction
Display
Navigation
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
Alice Hansen
Standup09:00 AM09:30 AM
Design review11:00 AM12:30 PM
Lunch w/ client01:00 PM02:00 PM
Late sync05:30 PM07:00 PM
Bob Nilsen
Sprint planning09:30 AM11:00 AM
1-on-1 w/ Carol02:00 PM02:30 PM
Deploy prep04:00 PM05:30 PM
On-call handover06:00 PM06:30 PM
Carol Berg
Client call10:00 AM11:00 AM
QA session01:00 PM03:00 PM
Retro04:30 PM05:30 PM
Dave Lund
Architecture sync09:00 AM10:30 AM
Code review01:30 PM03:00 PM
Team coffee03:30 PM04:00 PM
Evening standup06:00 PM06:30 PM

Getting started

Install the package from npm:

npm install @widgetkit/scheduler-react
# or
pnpm add @widgetkit/scheduler-react

Import the component and its stylesheet:

import { TimelineScheduler } from "@widgetkit/scheduler-react";
import "@widgetkit/scheduler-react/styles.css";

Pass resources, items, and date — those are the only required props. Enable interactions with boolean flags:

import { useState } from "react";
import { TimelineScheduler } from "@widgetkit/scheduler-react";
import "@widgetkit/scheduler-react/styles.css";
import type { Resource, TimelineItem } from "@widgetkit/scheduler-react";

const resources: Resource[] = [
  { id: "alice", name: "Alice Hansen" },
  { id: "bob",   name: "Bob Nilsen" },
];

const initialItems: TimelineItem[] = [
  {
    id: "1",
    resourceId: "alice",
    name: "Team standup",
    color: "#6c63ff",
    start: new Date("2024-06-10T09:00"),
    end:   new Date("2024-06-10T09:30"),
  },
];

export function App() {
  const [items, setItems] = useState(initialItems);
  const [date, setDate] = useState(new Date());

  return (
    <TimelineScheduler
      resources={resources}
      items={items}
      date={date}
      startHour={8}
      endHour={20}
      draggable
      resizable
      creatable
      showDateNav
      showZoomControls
      showNowLine
      showTooltip
      onItemsChange={setItems}
      onDateChange={setDate}
    />
  );
}

Drag & drop

Enable draggable to move events across resources and time. resizable adds handles on both edges. creatable lets users draw new events by clicking and holding on empty space. All interactions snap to snapMinutes (default 15).

Callbacks like onItemDragEnd, onItemResizeEnd, and onItemCreate fire with the updated position so you can persist changes to your backend.

08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
AH
Alice Hansen
2 events
Standup09:00 AM09:30 AM
Design review11:00 AM12:30 PM
BN
Bob Nilsen
2 events
Sprint planning09:30 AM11:00 AM
Client call01:00 PM02:00 PM
CB
Carol Berg
2 events
Code review10:00 AM11:30 AM
Deploy prep02:30 PM04:00 PM
<TimelineScheduler
  resources={resources}
  items={items}
  date={date}
  draggable
  resizable
  creatable
  snapMinutes={15}
  startHour={8}
  endHour={18}
  showDateNav
  showZoomControls
  showNowLine
  showTooltip
  onItemsChange={setItems}
  onDateChange={setDate}
  onItemDragEnd={({ item }) =>
    console.log(`Moved "${item.name}" to ${item.start.toLocaleTimeString()}`)
  }
  onItemResizeEnd={({ item }) =>
    console.log(`Resized "${item.name}": ${item.start} – ${item.end}`)
  }
  onItemCreate={({ resourceId, start }) =>
    console.log(`New event for ${resourceId} at ${start}`)
  }
/>

Custom rendering

The renderItem prop replaces the default event card with whatever React you return. The full TimelineItem object is passed in, including the optional description field — useful for showing notes, metadata, or custom badges inside events.

08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
AH
Alice Hansen
2 events
Sprint planning
Q2 roadmap · 8 participants
Lunch w/ client
Acme Corp · Restaurant Hygge
BN
Bob Nilsen
2 events
Code review
PR #142 — auth refactor
1-on-1
With Carol — performance review
CB
Carol Berg
2 events
Design sync
Mobile onboarding flow
QA session
Release 2.4.0 — regression suite
DL
Dave Lund
2 events
Architecture review
Data pipeline redesign
Team standup
Daily · all teams
function renderItem(item: TimelineItem) {
  return (
    <div style={{ display: "flex", height: "100%", overflow: "hidden" }}>
      <div
        style={{ width: 3, flexShrink: 0, background: item.color }}
      />
      <div style={{ padding: "3px 6px", overflow: "hidden" }}>
        <div style={{ fontWeight: 600, fontSize: 12, whiteSpace: "nowrap",
          overflow: "hidden", textOverflow: "ellipsis" }}>
          {item.name}
        </div>
        {item.description && (
          <div style={{ fontSize: 11, opacity: 0.7, whiteSpace: "nowrap",
            overflow: "hidden", textOverflow: "ellipsis" }}>
            {item.description}
          </div>
        )}
      </div>
    </div>
  );
}

<TimelineScheduler
  resources={resources}
  items={items}
  date={date}
  renderItem={renderItem}
  draggable
  resizable
  startHour={8}
  endHour={18}
  onItemsChange={setItems}
  onDateChange={setDate}
/>

Read only

Pass readonly to disable all interactions. The scheduler becomes a pure display component — useful for dashboards, public calendars, or embedding in contexts where editing isn't appropriate. Combine with showAvatar to show resource photos alongside their rows.

08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
Alice Hansen
3 events
Standup09:00 AM09:30 AM
Product review11:00 AM12:00 PM
All-hands03:00 PM04:00 PM
Bob Nilsen
3 events
Sprint planning09:30 AM11:00 AM
Lunch w/ client12:00 PM01:00 PM
All-hands03:00 PM04:00 PM
Carol Berg
2 events
Design sync10:00 AM11:30 AM
QA session01:00 PM03:00 PM
Dave Lund
2 events
Architecture review09:00 AM10:30 AM
Code review01:30 PM03:00 PM
Eva Strand
2 events
Onboarding09:00 AM12:00 PM
Team coffee02:00 PM02:30 PM
const resources: Resource[] = [
  { id: "r1", name: "Alice Hansen", avatar: "https://..." },
  { id: "r2", name: "Bob Nilsen",   avatar: "https://..." },
];

<TimelineScheduler
  resources={resources}
  items={items}
  date={date}
  readonly
  showDateNav
  showZoomControls
  showAvatar
  showNowLine
  showTooltip
  startHour={8}
  endHour={18}
  onDateChange={setDate}
/>

Props

All props except resources, items, and date are optional. Props marked * are required.

PropTypeDefaultDescription
Data
resources*Resource[]Array of resources rendered as rows
items*TimelineItem[]Array of events to display
date*DateCurrently displayed date
Time range
startHournumber0First visible hour (0–23)
endHournumber24Last visible hour (1–24)
snapMinutesnumber15Snap interval for drag and create interactions
Interaction
draggablebooleantrueAllow moving events to a new time or resource
resizablebooleanfalseAllow resizing events by dragging their edges
creatablebooleanfalseAllow creating events by clicking and dragging on empty space
editablebooleantrueAllow editing events via the built-in modal
readonlybooleanfalseDisable all interactions — overrides draggable, resizable, creatable, editable
minDurationMinutesnumber0Minimum event duration in minutes (0 = no limit)
maxDurationMinutesnumber0Maximum event duration in minutes (0 = no limit)
Display
zoom1 | 2 | 41Horizontal zoom level
showNavbooleanfalseShow the built-in navigation bar
showDateNavbooleantrueShow previous / next day arrows
showZoomControlsbooleantrueShow zoom level buttons
showTimebooleantrueShow time label inside events
showAvatarbooleantrueShow resource avatar in the row header
showEventCountbooleantrueShow count badge when events overlap
showTooltipbooleantrueShow hover tooltip with event details
showNowLinebooleantrueShow live indicator for the current time
Custom rendering
renderItem(item: TimelineItem) => ReactNodeReplaces the default event card with custom content
Callbacks
onItemsChange(items: TimelineItem[]) => voidFull updated items array after any change
onDateChange(date: Date) => voidDisplayed date changed
onZoomChange(zoom: 1 | 2 | 4) => voidZoom level changed
onItemCreate(detail: ItemCreateDetail) => voidNew event drawn by the user
onItemClick(detail: ItemClickDetail) => voidEvent single-clicked
onItemDblClick(detail: ItemDblClickDetail) => voidEvent double-clicked
onItemHover(detail: ItemHoverDetail) => voidMouse entered or left an event
onItemContextMenu(detail: ItemContextMenuDetail) => voidEvent right-clicked
onItemDragStart(detail: ItemDragStartDetail) => voidDrag interaction began
onItemDragEnd(detail: ItemDragEndDetail) => voidDrag ended with new resourceId, start, end
onItemResizeStart(detail: ItemResizeStartDetail) => voidResize interaction began
onItemResizeEnd(detail: ItemResizeEndDetail) => voidResize ended with new start and end

TypeScript types

All types are exported from both @widgetkit/scheduler-react and @widgetkit/scheduler-vue.

import type {
  Resource,
  TimelineItem,
  ItemCreateDetail,
  ItemClickDetail,
  ItemDblClickDetail,
  ItemHoverDetail,
  ItemContextMenuDetail,
  ItemDragStartDetail,
  ItemDragEndDetail,
  ItemResizeStartDetail,
  ItemResizeEndDetail,
} from "@widgetkit/scheduler-react";
// Core data types
interface Resource {
  id:      string;
  name:    string;
  avatar?: string;
}

interface TimelineItem {
  id:           string;
  resourceId:   string;
  name:         string;
  color:        string;
  start:        Date;
  end:          Date;
  description?: string;
}

// Callback detail types
interface ItemCreateDetail      { resourceId: string; start: Date; end: Date }
interface ItemClickDetail       { item: TimelineItem }
interface ItemDblClickDetail    { item: TimelineItem }
interface ItemHoverDetail       { item: TimelineItem; type: "enter" | "leave" }
interface ItemContextMenuDetail { item: TimelineItem; x: number; y: number }
interface ItemDragStartDetail   { item: TimelineItem }
interface ItemDragEndDetail     { item: TimelineItem; resourceId: string; start: Date; end: Date }
interface ItemResizeStartDetail { item: TimelineItem }
interface ItemResizeEndDetail   { item: TimelineItem; start: Date; end: Date }

Keyboard shortcuts

When an event is selected, the following keyboard shortcuts are available. Interactions respect readonly — shortcuts are disabled when it is set.

Move selected event by one snap interval
Move selected event to the previous / next resource
Delete
Delete the selected event
Escape
Deselect event / close edit modal