Skip to content

Remote Data

Use remote when events or resources are too large to ship up front, or when the scheduler must load only the visible date range.

For a comparison of host-owned full saves, host-owned incremental saves, and remote-backed saves, see Data ownership and save flows.

grid.eventScheduler = {
view: 'resourceTimeline',
weekStartDate: '2026-06-08',
remote: {
enabled: true,
loadEvents: async ({ dateRange, resourceIds }) => {
const params = new URLSearchParams({
start: dateRange.start,
end: dateRange.end,
resources: resourceIds?.join(',') ?? '',
});
const response = await fetch(`/api/scheduler/events?${params}`);
return { events: await response.json() };
},
loadResources: async () => {
const response = await fetch('/api/scheduler/resources');
return { resources: await response.json() };
},
commitMutation: async (request) => {
const response = await fetch('/api/scheduler/mutations', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error('Save failed');
}
return { accepted: true, events: await response.json() };
},
},
};

The resource timeline demo includes remote-mode controls for refresh, load-more resources, and simulated save failure.

Remote mode can load events, resources, availability, unassigned demand, and coverage for the visible range. It can also validate and commit accepted mutations.

const eventScheduler = {
view: 'resourceTimeline',
weekStartDate: '2026-06-08',
dateRange: { start: '2026-06-08', end: '2026-06-14' },
remote: {
enabled: true,
mode: 'remote',
debounceMs: 150,
overscanDays: 1,
resourcePageSize: 50,
loadEvents: ({ dateRange, filters, signal }) =>
api.loadEvents({ dateRange, filters }, { signal }),
loadResources: ({ cursor, limit, signal }) =>
api.loadResources({ cursor, limit }, { signal }),
loadAvailability: ({ dateRange, signal }) =>
api.loadAvailability({ dateRange }, { signal }),
loadCoverage: ({ dateRange, signal }) =>
api.loadCoverage({ dateRange }, { signal }),
validateMutation: ({ action, event, previousEvent }) =>
api.validateScheduleChange({ action, event, previousEvent }),
commitMutation: ({ action, event, previousEvent, events }) =>
api.saveScheduleChange({ action, event, previousEvent, events }),
optimistic: true,
rollbackOnError: true,
refreshPolicy: 'visible-range',
},
};

Remote modes:

ModeBehavior
localRemote controller is effectively off unless enabled hooks are used by the host.
remoteVisible data is loaded through remote hooks.
hybridHost can combine local side-channel data with remote range data.

Remote state is available from scheduler.getRemoteState(). The plugin emits:

EventPurpose
event-scheduler-remote-load-startA visible-range load begins.
event-scheduler-remote-load-successA load target resolves.
event-scheduler-remote-load-errorA load target fails.
event-scheduler-remote-mutation-pendingA remote mutation starts.
event-scheduler-remote-mutation-committedThe backend accepts a mutation.
event-scheduler-remote-mutation-rejectedThe backend rejects a mutation.
event-scheduler-remote-state-changeLoading, error, pending, paging, or empty state changes.

Use scheduler.refreshVisibleRange() to reload the current range and scheduler.loadMoreResources() when paged resources report hasMoreResources.