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 Appearance
Section titled “Conflict Appearance”Conflict detection and conflict presentation are configured separately. Keep rules under conflicts, and choose how detected conflicts look under customization.conflicts.appearance:
grid.eventScheduler = { view: 'resourceTimeline', weekStartDate: '2026-08-17', conflicts: { enabled: true, policy: 'mark', scope: 'same-resource', }, customization: { conflicts: { appearance: 'fill', }, },};| Appearance | Behavior |
|---|---|
outline | Keeps the existing conflict rings around the event. This is the default for backward compatibility. |
fill | Uses the conflict severity background and text colors inside the event bar without the oversized conflict rings. |
Filled conflicts keep the warning or conflict state icon and the normal event shadow. Selection and keyboard focus indicators are unchanged, so fill removes only the conflict decoration rings—not accessibility or interaction feedback. The option works across scheduler views; it is especially useful in dense resource timelines where multiple outlines would consume lane space.
You can combine the built-in appearance with customization.conflicts.className, properties, indicator, and tooltip when a product needs additional presentation hooks. These hooks do not change whether a conflict blocks a mutation.
Preventing Mutations
Section titled “Preventing Mutations”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.