Skip to content

Conflicts

Conflict detection can mark overlaps, warn users, or block changes through the mutation lifecycle. This is also how you forbid events outside working hours: configure working time with calendars or eventSchedulerAvailability, then make the resulting outside-availability conflict blocking.

For a detailed explanation of how availability and calendar inputs become outside-availability and blocked-time, see the Scheduling Rules overview.

grid.eventScheduler = {
view: 'resourceTimeline',
weekStartDate: '2026-06-08',
conflicts: {
enabled: true,
policy: 'mark',
scope: 'same-resource',
minDurationMinutes: 30,
rules: {
overlap: 'error',
'outside-availability': 'error',
'blocked-time': 'error',
},
},
validateMutation: (detail) => {
const blocking = detail.conflicts?.some((conflict) => conflict.severity === 'error');
if (blocking) {
return 'This change creates a blocking scheduling conflict.';
}
},
};

policy sets the default behavior for all conflict types:

| Policy | Behavior | | :-- | :-- | | mark | Show conflicts as warnings and allow the mutation unless a specific rule is blocking. | | prevent | Treat conflicts as blocking errors by default. | | allow | Ignore conflicts unless a specific rule overrides the type. |

rules override the default policy per conflict type. Use error to reject the mutation, warning to mark it but allow it, confirm when your product wants a confirmation flow, and ignore to hide the conflict.

Common blocking rules:

conflicts: {
enabled: true,
policy: 'mark',
rules: {
overlap: 'error',
'outside-availability': 'error',
'blocked-time': 'error',
'invalid-duration': 'error',
},
}

outside-availability is raised when an event falls outside calendar working days/hours or outside explicit kind: 'working' availability intervals. blocked-time is raised when an event overlaps explicit blocked, holiday, or break availability. nonWorkingTime alone is visual and does not create blocking conflicts.

Conflict rules are the declarative way to stop invalid changes. When a conflict resolves to severity: 'error', the scheduler rejects the mutation before it updates eventSchedulerEvents.

For business rules that need code, use the same cancelable event pattern as RevoGrid editing. Listen to event-scheduler-before-event-change and call preventDefault() before the local commit:

grid.addEventListener('event-scheduler-before-event-change', (event) => {
const hasBlockingConflict = event.detail.conflicts?.some((conflict) =>
conflict.severity === 'error' &&
event.detail.eventId !== null &&
conflict.eventIds.includes(event.detail.eventId)
);
if (hasBlockingConflict || event.detail.event?.locked) {
event.preventDefault();
}
});

validateMutation is the config-level equivalent for synchronous validation. Return false or a string to reject the same pending mutation.

const eventScheduler = {
validateMutation: (detail) => {
if (detail.changes.resourceId === 'locked-room') {
return 'That room cannot accept bookings.';
}
},
};

Do not use event-scheduler-event-changed for prevention. event-scheduler-event-created, event-scheduler-event-changed, and event-scheduler-event-deleted fire after the scheduler has accepted the mutation; use those events to persist or sync the emitted detail.events array.

Listen to conflict updates for side panels, badges, filters, or exports.

grid.addEventListener('event-scheduler-conflicts-updated', (event) => {
renderConflictPanel(event.detail.conflicts);
});

Event blocks expose conflict classes and data attributes for custom styles.