Skip to content

Examples

This page collects small, product-neutral Event Scheduler examples. The same APIs work for shifts, bookings, appointments, staffing, rooms, maintenance, tasks, equipment, and team planning.

The runnable demo catalog includes these complete scheduler stories:

| Demo | Use it for | | --- | --- | | Event Staff Scheduler | Resource timeline, coverage, conflicts, templates, saved views, and remote-mode controls. | | Employee Shift Planner | Week view with days on the x-axis, time on the y-axis, drag-create, move, resize, delete, and closed hours. | | Room Booking Scheduler | Resource booking with availability windows, blocked time, conflicts, and grouped resources. | | Equipment / Machine Scheduler | Maintenance planning, utilization, capacity rows, and exportable workload data. | | Developer Sprint Scheduler | Team/task planning, custom ranges, unassigned work, templates, and workload balancing. |

import {
EventSchedulerPlugin,
type EventSchedulerConfig,
type EventSchedulerEventEntity,
} from '@revolist/revogrid-enterprise';
const events: EventSchedulerEventEntity[] = [
{
id: 'booking-1',
title: 'Customer onboarding',
resourceId: 'room-a',
startDateTime: '2026-06-08T09:00:00.000Z',
endDateTime: '2026-06-08T11:00:00.000Z',
type: 'booking',
status: 'confirmed',
},
];
const eventScheduler: EventSchedulerConfig = {
view: 'week',
weekStartDate: '2026-06-08',
weekStartsOn: 1,
visibleDays: [1, 2, 3, 4, 5],
timeRange: { start: '06:00', end: '22:00' },
slotMinutes: 30,
locale: 'en-US',
timeZone: 'UTC',
editable: true,
allowCreate: true,
allowMove: true,
allowResize: true,
allowDelete: true,
todayHighlight: true,
showCurrentTimeMarker: true,
};
grid.plugins = [EventSchedulerPlugin];
grid.eventScheduler = eventScheduler;
grid.eventSchedulerEvents = events;
grid.eventScheduler = {
...grid.eventScheduler,
eventProperties: ({ event, isSelected, hasConflict, isLocked }) => ({
class: [
'planner-event',
isSelected ? 'planner-event--selected' : '',
hasConflict ? 'planner-event--conflict' : '',
isLocked ? 'planner-event--locked' : '',
].filter(Boolean).join(' '),
style: {
'--planner-event-accent': event.color ?? '#2563eb',
},
}),
eventContentTemplate: (h, { event, resource, start, end, isShortEvent }) =>
h('span', { class: 'planner-event__body' }, [
h('strong', { class: 'planner-event__title' }, event.title),
isShortEvent ? null : h('span', { class: 'planner-event__time' }, `${start.slice(11, 16)}-${end.slice(11, 16)}`),
resource ? h('small', { class: 'planner-event__resource' }, resource.name) : null,
]),
eventTooltipTemplate: ({ event, resource, duration }) =>
`${event.title}\n${resource?.name ?? 'Unassigned'}\n${duration} minutes`,
};
grid.eventScheduler = {
...grid.eventScheduler,
dayHeaderFormatter: (date) =>
new Intl.DateTimeFormat('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric',
timeZone: 'UTC',
}).format(date),
dayHeaderTemplate: (h, context) =>
h('span', { class: 'planner-day-header' }, [
h('strong', null, context.defaultLabel),
context.today ? h('span', { class: 'planner-day-header__badge' }, 'Today') : null,
context.holiday ? h('span', { class: 'planner-day-header__holiday' }, context.holidayLabel) : null,
h('small', null, `${context.eventCount ?? 0} events`),
]),
dayHeaderProperties: ({ today, weekend, holiday }) => ({
class: [
today ? 'planner-day-header--today' : '',
weekend ? 'planner-day-header--weekend' : '',
holiday ? 'planner-day-header--holiday' : '',
].filter(Boolean).join(' '),
}),
};
grid.eventScheduler = {
...grid.eventScheduler,
contextMenu: {
enabled: true,
getSlotContextMenuItems: ({ slot }) => slot
? [
{
id: 'create-booking',
label: 'Create booking here',
shortcut: 'C',
action: () => createBooking(slot),
},
{
id: 'hold-slot',
label: 'Block this time',
group: 'availability',
action: () => createHold(slot),
},
]
: [],
onContextMenuAction: ({ itemId, target, slot }) =>
analytics.track('scheduler_menu_action', { itemId, target, slot }),
},
};
grid.eventScheduler = {
...grid.eventScheduler,
showCurrentTimeMarker: true,
currentTimeUpdateInterval: 60_000,
currentTimeLabelFormatter: ({ dateTime }) =>
new Intl.DateTimeFormat('en-US', {
hour: 'numeric',
minute: '2-digit',
}).format(new Date(dateTime)),
currentTimeMarkerProperties: ({ orientation }) => ({
class: `planner-now planner-now--${orientation}`,
style: {
'--event-scheduler-current-time-color': '#dc2626',
},
}),
currentTimeMarkerTemplate: (h, { label }) =>
h('span', { class: 'planner-now__label' }, label),
};
grid.eventScheduler = {
...grid.eventScheduler,
workingHours: {
byDay: {
1: { start: '08:00', end: '18:00' },
2: { start: '08:00', end: '18:00' },
3: { start: '08:00', end: '18:00' },
4: { start: '08:00', end: '18:00' },
5: { start: '08:00', end: '16:00' },
0: false,
6: false,
},
},
closedHours: [
{
id: 'lunch',
days: [1, 2, 3, 4, 5],
ranges: { start: '12:00', end: '13:00' },
title: 'Closed',
reason: 'Lunch break',
},
],
closedSlotTooltip: ({ title, reason }) =>
[title, reason].filter(Boolean).join(' - '),
closedSlotTemplate: (h, { reason }) =>
h('span', { class: 'planner-slot__closed' }, reason ?? 'Closed'),
isTimeSlotAvailable: ({ date, startMinutes }) =>
date !== '2026-06-19' && startMinutes >= 8 * 60,
};
grid.eventScheduler = {
...grid.eventScheduler,
editable: false,
allowCreate: false,
allowMove: false,
allowResize: false,
allowDelete: false,
keyboardShortcuts: {
enabled: true,
shortcuts: {
copy: 'Mod+c',
paste: false,
duplicate: false,
delete: false,
lock: false,
},
},
};

Read-only mode keeps rendering, selection, tooltips, context menus, and custom templates active while blocking scheduler mutations.

let selectedEventIds: readonly string[] = [];
grid.addEventListener('event-scheduler-before-event-select', (event) => {
event.preventDefault();
selectedEventIds = event.detail.eventIds.map(String);
renderScheduler();
});
function renderScheduler() {
grid.eventScheduler = {
...grid.eventScheduler,
selectionMode: 'multiple',
selection: {
selectedEventIds,
clipboard: true,
onChange: ({ eventIds }) => {
selectedEventIds = eventIds.map(String);
renderScheduler();
},
},
onSelectionChange: ({ events }) =>
inspector.show(events),
onEventMove: ({ events }) => {
grid.eventSchedulerEvents = [...events];
void saveEvents(events);
},
};
}

Use the before-select event when selection lives in application state. Prevent the pending scheduler selection, update host state, then pass the accepted ids back through selection.selectedEventIds. Event arrays, visible ranges, and filters follow the same ownership model: the scheduler emits callbacks and the host updates eventScheduler, eventSchedulerEvents, and related props.

.booking-scheduler revo-grid {
--event-scheduler-accent: #0f766e;
--event-scheduler-accent-soft: #ccfbf1;
--event-scheduler-grid-line: #d8dee6;
--event-scheduler-current-time-color: #dc2626;
--event-scheduler-weekend-bg: rgba(15, 118, 110, 0.05);
--event-scheduler-event-bg: #eef7f3;
--event-scheduler-event-color: #0f766e;
}
.planner-event--conflict {
--event-scheduler-event-color: #b45309;
--event-scheduler-event-bg: #fff7ed;
}
.planner-day-header--today {
box-shadow: inset 0 -2px 0 var(--event-scheduler-accent);
}

Use CSS variables for broad theme changes. Use eventProperties, dayHeaderProperties, slotProperties, closedSlotProperties, and currentTimeMarkerProperties when styling depends on scheduler state.