---
title: "React Event calendar Bulk Actions Example | Mobiscroll"
component: "Event calendar"
framework: "React"
library_version: "6.2.0"
url: "https://demo.mobiscroll.com/react/eventcalendar/event-bulk-actions-edit-delete-update"
---

# Bulk Actions - React Event calendar demo

Multi-select events by keyboard in a React calendar - bulk delete, update, or run custom actions via action panel or right-click context menu. For React and Next.JS.

- [Live demo](https://demo.mobiscroll.com/react/eventcalendar/event-bulk-actions-edit-delete-update)
- [View React JS code on GitHub](https://github.com/acidb/mobiscroll-demos-react/tree/main/src/demos/eventcalendar/calendar-view/event-bulk-actions-edit-delete-update)
- [View React TS code on GitHub](https://github.com/acidb/mobiscroll-demos-react-ts/tree/main/src/demos/eventcalendar/calendar-view/event-bulk-actions-edit-delete-update)
- This demo in other frameworks: [JavaScript](https://demo.mobiscroll.com/javascript/eventcalendar/event-bulk-actions-edit-delete-update.md), [jQuery](https://demo.mobiscroll.com/jquery/eventcalendar/event-bulk-actions-edit-delete-update.md), [Angular](https://demo.mobiscroll.com/angular/eventcalendar/event-bulk-actions-edit-delete-update.md), [Vue](https://demo.mobiscroll.com/vue/eventcalendar/event-bulk-actions-edit-delete-update.md)

## Description

Multiple event selection can be turned on with the [`selectMultipleEvents`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-selectMultipleEvents) option. Selecting multiple events can be done through user interaction with `CTRL`/`SHIFT`/`CMD` + click or programmatically eg. click of a button or 'select all' checkbox.

Bulk operations like delete, update can be applied the selected events. Things like deleting with the `backspace` or `delete` buttons work out of the box but custom actions can be also applied. The selection can be easily retrieved and updated with the [`selectedEvents`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-selectedEvents) option and the [`onSelectedEventsChange`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onSelectedEventsChange) event.

Custom actions can be performed with external buttons or with context menu activated on right-click.

**Interested in moving events between two separate calendars?** [Check out the next example for cut, copy, paste events →](https://demo.mobiscroll.com/react/eventcalendar/cut-copy-paste-events-between-calendars)

## What this demo shows

- A desktop month view event calendar that supports selecting and managing multiple events from the current view.
- **Calendar** A full month calendar displays multiple events, with all events visible and row height expanding to fit the content.
- **Calendar header** The header shows the current month and year on the left, and blue month navigation arrows with a `Today` button (for jumping back to the current date) between them on the right. 
- **Bulk action panel** A side panel next to the calendar contains `Select all from view`, `Reset selection`, and `Update selected` actions.
- **Select all from view** Clicking this action selects all events currently visible and highlights thei titles in the month view and shows a bottom-centered toast message: `All events selected from the view`.
- **Reset selection** Clicking this action clears the current selection and shows a bottom-centered toast message: `Selection cleared`.
- **Update selected** Clicking this action changes the color of all selected events to orange and shows a bottom-centered toast message: `All selected event's color changed to orange`.
- **Currently selected** The side panel includes a `Currently selected` section that is empty by default and lists the titles of all selected events when one or more events are selected.
- **Multi-selection** Users can select multiple events with `CTRL`/`SHIFT`/`CMD` + click.
- **Context menu** Right-clicking an event opens a small popup with `Update` and `Delete` actions for that event.
- **Label interaction** Hovering over or clicking an event label selects it and highlights the selected label.
- **Day cell states for future days** Hovering a day cell highlights the day number with a gray background, while clicking the empty part of the cell selects the day and highlights the day number with a blue background.

## Best for

- **Scheduling tools** Calendar-based scheduling interfaces where users need to review and manage many visible events in a month view.
- **Resource management** Resource planning workflows that require selecting and adjusting multiple bookings, assignments, or availability blocks together.
- **Operations teams** Internal tools for managing appointments, team shifts, project deadlines, or other time-based records in batches.
- **Mixed individual and batch actions** Use cases that need both multi-event actions and single-event actions from a contextual right-click menu.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-themeVariant) - option
- [`labels`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-labels) - option
- [`getEvents`](https://mobiscroll.com/docs/react/eventcalendar/api#method-getEvents) - method
- [`clickToCreate`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-dragToResize) - option
- [`eventDelete`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-eventDelete) - option
- [`data`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-data) - option
- [`view`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-view) - option
- [`selectMultipleEvents`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-selectMultipleEvents) - option
- [`selectedEvents`](https://mobiscroll.com/docs/react/eventcalendar/api#opt-selectedEvents) - option
- [`onEventDelete`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onEventDelete) - event
- [`onEventUpdate`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onEventUpdate) - event
- [`onEventRightClick`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onEventRightClick) - event
- [`onPageLoading`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onPageLoading) - event
- [`onSelectedEventsChange`](https://mobiscroll.com/docs/react/eventcalendar/api#event-onSelectedEventsChange) - event
- [`inputProps`](https://mobiscroll.com/docs/react/select/api#opt-inputProps) - option
- [`display`](https://mobiscroll.com/docs/react/select/api#opt-display) - option
- [`touchUi`](https://mobiscroll.com/docs/react/select/api#opt-touchUi) - option
- [`anchor`](https://mobiscroll.com/docs/react/select/api#opt-anchor) - option
- [`data`](https://mobiscroll.com/docs/react/select/api#opt-data) - option
- [`isOpen`](https://mobiscroll.com/docs/react/select/api#opt-isOpen) - option
- [`onChange`](https://mobiscroll.com/docs/react/select/api#event-onChange) - event
- [`onClose`](https://mobiscroll.com/docs/react/select/api#event-onClose) - event
- [`onClose`](https://mobiscroll.com/docs/react/notifications/confirm#event-onClose) - event
- [`onClose`](https://mobiscroll.com/docs/react/notifications/toast#event-onClose) - event

## Code

### JSX

```jsx
import { Button, Confirm, Eventcalendar, formatDate, getJson, Page, Select, setOptions, Toast } from '@mobiscroll/react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

function App() {
  const [myEvents, setMyEvents] = useState([]);
  const [mySelectedEvents, setSelectedEvents] = useState([]);
  const [firstDay, setFirstDay] = useState();
  const [lastDay, setLastDay] = useState();
  const [menuAnchor, setMenuAnchor] = useState();
  const [menuOpen, setMenuOpen] = useState(false);
  const [selectedValue, setSelected] = useState(false);
  const [confirmOpen, setConfirmOpen] = useState(false);
  const [toastOpen, setToastOpen] = useState(false);
  const [confirmMessage, setConfirmMessage] = useState('');
  const [toastMessage, setToastMessage] = useState('');

  const calRef = useRef();

  const view = useMemo(() => ({ calendar: { labels: 'all' } }), []);

  const selectData = useMemo(
    () => [
      { text: 'Update', value: 'update' },
      { text: 'Delete', value: 'delete' },
    ],
    [],
  );

  const deleteSelectedEvents = useCallback(() => {
    setConfirmMessage(mySelectedEvents.map((e) => e.title).join(', '));
    setConfirmOpen(true);
  }, [mySelectedEvents]);

  const updateSelectedEvents = useCallback(() => {
    const events = mySelectedEvents.length === 0 ? [mySelectedEvents] : mySelectedEvents;
    let eventsToUpdate = [...myEvents];

    for (const event of events) {
      if (event.recurring) {
        const origEvent = eventsToUpdate.find((x) => x.id === event.original.id);
        const newEvent = {
          ...event,
          recurring: undefined,
          color: 'orange',
          id: event.id + '_' + formatDate('YYYY-MM-DD', event.start),
        };
        eventsToUpdate = [...eventsToUpdate, newEvent];
        const updatedOrigEvent = {
          ...origEvent,
          recurringException: [...(origEvent.recurringException || []), event.start],
        };
        const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id);
        eventsToUpdate.splice(index, 1, updatedOrigEvent);
      } else {
        const newEv = { ...event };
        newEv.color = 'orange';
        const index = eventsToUpdate.findIndex((x) => x.id === newEv.id);
        eventsToUpdate.splice(index, 1, newEv);
      }
    }

    setToastMessage("All selected event's color changed to orange");
    setToastOpen(true);
    setMyEvents(eventsToUpdate);
    setSelectedEvents([]);
  }, [myEvents, mySelectedEvents]);

  const handleToastClose = useCallback(() => {
    setToastOpen(false);
  }, []);

  const handleConfirmClose = useCallback(
    (result) => {
      if (result) {
        let eventsToUpdate = [...myEvents];

        for (const event of mySelectedEvents) {
          if (event.recurring) {
            const origEvent = eventsToUpdate.find((x) => x.id === event.original.id);
            const updatedOrigEvent = {
              ...origEvent,
              recurringException: [...(origEvent.recurringException || []), event.start],
            };
            const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id);
            eventsToUpdate.splice(index, 1, updatedOrigEvent);
          } else {
            eventsToUpdate = eventsToUpdate.filter((ev) => ev.id !== event.id);
          }
        }

        setMyEvents(eventsToUpdate);
        setSelectedEvents([]);
        setToastMessage('Deleted');
        setToastOpen(true);
      }
      setConfirmOpen(false);
    },
    [myEvents, mySelectedEvents],
  );

  const handleEventUpdate = useCallback(
    (args) => {
      if (args.isDelete) {
        if (!confirmOpen) {
          deleteSelectedEvents();
        }
        return false;
      }
    },
    [confirmOpen, deleteSelectedEvents],
  );

  const handleEventDelete = useCallback(() => {
    if (!confirmOpen) {
      deleteSelectedEvents();
      return false;
    }
  }, [confirmOpen, deleteSelectedEvents]);

  const handlePageLoading = useCallback((args) => {
    setTimeout(() => {
      setFirstDay(args.firstDay);
      setLastDay(args.lastDay);
    });
  }, []);

  const handleSelectedEventsChange = useCallback((args) => {
    setSelectedEvents(args.events);
  }, []);

  const handleEventRightClick = useCallback((args) => {
    args.domEvent.preventDefault();
    setMenuAnchor(args.domEvent.target);
    setTimeout(() => {
      setMenuOpen(true);
    });
  }, []);

  const selectAllEvents = useCallback(() => {
    const selectedEvents = calRef.current.getEvents(firstDay, lastDay);
    setSelectedEvents(selectedEvents);
    setToastMessage('All events selected from view');
    setToastOpen(true);
  }, [firstDay, lastDay]);

  const resetSelection = useCallback(() => {
    setSelectedEvents([]);
    setToastMessage('Selection cleared');
    setToastOpen(true);
  }, []);

  const handleSelectChange = useCallback(
    (args) => {
      setSelected(args.value);
      if (args.value === 'update') {
        updateSelectedEvents();
      } else if (args.value === 'delete') {
        deleteSelectedEvents();
      }
    },
    [deleteSelectedEvents, updateSelectedEvents],
  );

  const handleSelectClose = useCallback(() => {
    setSelected('');
    setMenuOpen(false);
  }, []);

  const handleDeleteKey = useCallback(
    (ev) => {
      if (!confirmOpen && (ev.keyCode === 8 || ev.keyCode === 46)) {
        deleteSelectedEvents();
      }
    },
    [confirmOpen, deleteSelectedEvents],
  );

  useEffect(() => {
    getJson(
      'https://trial.mobiscroll.com/events/?vers=5',
      (events) => {
        setMyEvents(events);
      },
      'jsonp',
    );
  }, []);

  return (
    <Page className="md-bulk-operations">
      <div className="mbsc-grid mbsc-no-padding" onKeyDown={handleDeleteKey}>
        <div className="mbsc-row">
          <div className="mbsc-col-sm-9 mbsc-push-sm-3">
            <Eventcalendar
              className="md-bulk-operations-border"
              ref={calRef}
              clickToCreate={true}
              dragToCreate={true}
              dragToMove={true}
              dragToResize={true}
              eventDelete={true}
              data={myEvents}
              view={view}
              selectMultipleEvents={true}
              selectedEvents={mySelectedEvents}
              onEventDelete={handleEventDelete}
              onEventUpdate={handleEventUpdate}
              onEventRightClick={handleEventRightClick}
              onPageLoading={handlePageLoading}
              onSelectedEventsChange={handleSelectedEventsChange}
            />
            <Select
              inputProps={{ type: 'hidden' }}
              display="anchored"
              touchUi={false}
              anchor={menuAnchor}
              data={selectData}
              value={selectedValue}
              isOpen={menuOpen}
              onChange={handleSelectChange}
              onClose={handleSelectClose}
            />
          </div>
          <div className="mbsc-col-sm-3 mbsc-pull-sm-9">
            <div className="mbsc-form-group">
              <div className="mbsc-button-group-block">
                <Button onClick={selectAllEvents}>Select all this month</Button>
                <Button onClick={resetSelection}>Reset selection</Button>
                <Button onClick={updateSelectedEvents}>Update selected</Button>
              </div>
            </div>
            <div className="mbsc-form-group-title">Currently selected</div>
            <div className="mbsc-padding md-selected-event-list">
              <ul>
                {mySelectedEvents.map((e, i) => (
                  <li key={i}>{e.title}</li>
                ))}
              </ul>
            </div>
          </div>
        </div>
      </div>
      <Confirm
        isOpen={confirmOpen}
        title="Are you sure you want to delete the following events?"
        message={confirmMessage}
        okText="Delete"
        onClose={handleConfirmClose}
      />
      <Toast isOpen={toastOpen} message={toastMessage} onClose={handleToastClose} />
    </Page>
  );
}

export default App;
```

### TSX

```tsx
import {
  Button,
  Confirm,
  Eventcalendar,
  formatDate,
  getJson,
  MbscCalendarEvent,
  MbscEventcalendarView,
  MbscEventClickEvent,
  MbscEventUpdateEvent,
  MbscPageLoadingEvent,
  MbscSelectChangeEvent,
  MbscSelectedEventsChangeEvent,
  Page,
  Select,
  setOptions,
  Toast,
} from '@mobiscroll/react';
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';

setOptions({
  theme: 'ios',
  themeVariant: 'light'
});

const App: FC = () => {
  const [myEvents, setEvents] = useState<MbscCalendarEvent[]>([]);
  const [mySelectedEvents, setSelectedEvents] = useState<MbscCalendarEvent[]>([]);
  const [firstDay, setFirstDay] = useState<Date>();
  const [lastDay, setLastDay] = useState<Date>();
  const [menuAnchor, setMenuAnchor] = useState();
  const [menuOpen, setMenuOpen] = useState<boolean>(false);
  const [selectedValue, setSelected] = useState<string>('');
  const [confirmOpen, setConfirmOpen] = useState<boolean>(false);
  const [toastOpen, setToastOpen] = useState<boolean>(false);
  const [confirmMessage, setConfirmMessage] = useState<string>('');
  const [toastMessage, setToastMessage] = useState<string>('');

  const calRef = useRef<Eventcalendar>(null);

  const myView = useMemo<MbscEventcalendarView>(() => ({ calendar: { labels: true } }), []);

  const selectData = useMemo(
    () => [
      { text: 'Update', value: 'update' },
      { text: 'Delete', value: 'delete' },
    ],
    [],
  );

  const deleteSelectedEvents = useCallback(() => {
    setConfirmMessage(mySelectedEvents.map((e) => e.title).join(', '));
    setConfirmOpen(true);
  }, [mySelectedEvents]);

  const updateSelectedEvents = useCallback(() => {
    const events = mySelectedEvents;
    let eventsToUpdate = [...myEvents];

    for (const event of events) {
      if (event.recurring) {
        const origEvent = event.original!;
        const newEvent = {
          ...event,
          recurring: undefined,
          color: 'orange',
          id: event.id + '_' + formatDate('YYYY-MM-DD', new Date(event.start as string)),
        };
        eventsToUpdate = [...eventsToUpdate, newEvent];
        const updatedOrigEvent = {
          ...origEvent,
          recurringException: [...((origEvent.recurringException as Array<string>) || []), event.start as string],
        };

        // Update the event in the list
        const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id);
        eventsToUpdate.splice(index, 1, updatedOrigEvent);
      } else {
        const newEv = { ...event };
        newEv.color = 'orange';
        const index = eventsToUpdate.findIndex((x) => x.id === newEv.id);
        eventsToUpdate.splice(index, 1, newEv);
      }
    }

    setToastMessage("All selected event's color changed to orange");
    setToastOpen(true);
    setEvents(eventsToUpdate);
    setSelectedEvents([]);
  }, [myEvents, mySelectedEvents]);

  const handleToastClose = useCallback(() => {
    setToastOpen(false);
  }, []);

  const handleConfirmClose = useCallback(
    (result: boolean) => {
      if (result) {
        let eventsToUpdate = [...myEvents];

        for (const event of mySelectedEvents) {
          if (event.recurring) {
            const origEvent = event.original!;
            const updatedOrigEvent = {
              ...origEvent,
              recurringException: [...((origEvent.recurringException as Array<string>) || []), event.start as string],
            };

            // Update the event in the list
            const index = eventsToUpdate.findIndex((x) => x.id === origEvent.id);
            eventsToUpdate.splice(index, 1, updatedOrigEvent);
          } else {
            eventsToUpdate = eventsToUpdate.filter((ev) => ev.id !== event.id);
          }
        }

        setEvents(eventsToUpdate);
        setSelectedEvents([]);
        setToastMessage('Deleted');
        setToastOpen(true);
      }
      setConfirmOpen(false);
    },
    [myEvents, mySelectedEvents],
  );

  const handleEventUpdate = useCallback(
    (args: MbscEventUpdateEvent) => {
      if (args.isDelete) {
        if (!confirmOpen) {
          deleteSelectedEvents();
        }
        return false;
      }
    },
    [confirmOpen, deleteSelectedEvents],
  );

  const handleEventDelete = useCallback(() => {
    if (!confirmOpen) {
      deleteSelectedEvents();
      return false;
    }
  }, [confirmOpen, deleteSelectedEvents]);

  const handlePageLoading = useCallback((args: MbscPageLoadingEvent) => {
    setTimeout(() => {
      setFirstDay(args.firstDay);
      setLastDay(args.lastDay);
    });
  }, []);

  const handleSelectedEventsChange = useCallback((args: MbscSelectedEventsChangeEvent) => {
    setSelectedEvents(args.events);
  }, []);

  const handleEventRightClick = useCallback((args: MbscEventClickEvent) => {
    args.domEvent.preventDefault();
    setMenuAnchor(args.domEvent.target);
    setTimeout(() => {
      setMenuOpen(true);
    });
  }, []);

  const selectAllEvents = useCallback(() => {
    const selectedEvents = calRef.current!.getEvents(firstDay, lastDay);
    setSelectedEvents(selectedEvents);
    setToastMessage('All events selected from view');
    setToastOpen(true);
  }, [firstDay, lastDay]);

  const resetSelection = useCallback(() => {
    setSelectedEvents([]);
    setToastMessage('Selection cleared');
    setToastOpen(true);
  }, []);

  const handleSelectChange = useCallback(
    (args: MbscSelectChangeEvent) => {
      setSelected(args.value);
      if (args.value === 'update') {
        updateSelectedEvents();
      } else if (args.value === 'delete') {
        deleteSelectedEvents();
      }
    },
    [deleteSelectedEvents, updateSelectedEvents],
  );

  const handleSelectClose = useCallback(() => {
    setSelected('');
    setMenuOpen(false);
  }, []);

  const handleDeleteKey = useCallback(
    (ev: { keyCode: number }) => {
      if (!confirmOpen && (ev.keyCode === 8 || ev.keyCode === 46)) {
        deleteSelectedEvents();
      }
    },
    [confirmOpen, deleteSelectedEvents],
  );

  useEffect(() => {
    getJson(
      'https://trial.mobiscroll.com/events/?vers=5',
      (events: MbscCalendarEvent[]) => {
        setEvents(events);
      },
      'jsonp',
    );
  }, []);

  return (
    <Page className="md-bulk-operations">
      <div className="mbsc-grid mbsc-no-padding" onKeyDown={handleDeleteKey}>
        <div className="mbsc-row">
          <div className="mbsc-col-sm-9 mbsc-push-sm-3">
            <Eventcalendar
              className="md-bulk-operations-border"
              ref={calRef}
              clickToCreate={true}
              dragToCreate={true}
              dragToMove={true}
              dragToResize={true}
              eventDelete={true}
              data={myEvents}
              view={myView}
              selectMultipleEvents={true}
              selectedEvents={mySelectedEvents}
              onEventDelete={handleEventDelete}
              onEventUpdate={handleEventUpdate}
              onEventRightClick={handleEventRightClick}
              onPageLoading={handlePageLoading}
              onSelectedEventsChange={handleSelectedEventsChange}
            />
            <Select
              inputProps={{ type: 'hidden' }}
              display="anchored"
              touchUi={false}
              anchor={menuAnchor}
              data={selectData}
              value={selectedValue}
              isOpen={menuOpen}
              onChange={handleSelectChange}
              onClose={handleSelectClose}
            />
          </div>
          <div className="mbsc-col-sm-3 mbsc-pull-sm-9">
            <div className="mbsc-form-group">
              <div className="mbsc-button-group-block">
                <Button onClick={selectAllEvents}>Select all this month</Button>
                <Button onClick={resetSelection}>Reset selection</Button>
                <Button onClick={updateSelectedEvents}>Update selected</Button>
              </div>
            </div>
            <div className="mbsc-form-group-title">Currently selected</div>
            <div className="mbsc-padding md-selected-event-list">
              <ul>
                {mySelectedEvents.map((e, i) => (
                  <li key={i}>{e.title}</li>
                ))}
              </ul>
            </div>
          </div>
        </div>
      </div>
      <Confirm
        isOpen={confirmOpen}
        title="Are you sure you want to delete the following events?"
        message={confirmMessage}
        okText="Delete"
        onClose={handleConfirmClose}
      />
      <Toast isOpen={toastOpen} message={toastMessage} onClose={handleToastClose} />
    </Page>
  );
};
export default App;
```

### CSS

```css
.md-bulk-operations-border {
  border-left: 1px solid #ccc;
}

.md-selected-event-list {
  height: 500px;
  overflow: auto;
  padding-top: 0;
  padding-bottom: 0;
}
```

## Implementation notes

- Use `view: { calendar: { labels: true } }`. Set `selectMultipleEvents: true` to enable Ctrl/Shift/Cmd+click multi-selection. Load events from `https://trial.mobiscroll.com/events/?vers=5` via JSONP. React/Angular/Vue: assign the fetched array to the state/property bound to the `data` prop. JS/jQuery: call `inst.setEvents(events)` on load.
- Use a two-column layout: the calendar occupies most of the space; the sidebar contains three action buttons and a scrollable list of selected event titles. Button labels: React/Vue use "Select all this month"; Angular/JS/jQuery use "Select all from view"; all frameworks use "Reset selection" and "Update selected".
- Track `firstDay` and `lastDay` via `onPageLoading` (`args.firstDay`, `args.lastDay`). `onSelectedEventsChange` → update the current selection and refresh the displayed titles list.
- **"Select all"**: call `inst.getEvents(firstDay, lastDay)` to retrieve visible events, then set them as selected; for the imperative API, call `inst.setSelectedEvents(events)`. Show toast.
- **"Reset selection"**: clear `selectedEvents`; for the imperative API, call `inst.setSelectedEvents([])`. Show toast.
- **"Update selected"**: iterate the selected events. For **recurring occurrences**: clear `recurring`, set `color = 'orange'`, append `'_' + formatDate('YYYY-MM-DD', event.start)` to the `id`, push `event.start` to `origEvent.recurringException`, then persist the detached event and the updated original. For **non-recurring**: set `color = 'orange'` and update in place. Update the events array in place; for the imperative API, call `inst.updateEvent(eventsToUpdate)` and `inst.addEvent(newEvents)`. Reset selection, show toast.
- **`deleteSelectedEvents()`**: show a confirm dialog ("Are you sure you want to delete the following events?" / okText: "Delete") with selected event titles. On confirm: for **recurring** events push `event.start` to `origEvent.recurringException` and update the original; for **non-recurring**, remove from the events array; for the imperative API, call `inst.removeEvent(eventsToDelete)` and `inst.setSelectedEvents([])`. Reset selection, show "Deleted" toast.
- **Delete entry points**: (1) **`onEventDelete`** - `return false` (all frameworks); (2) **`onEventUpdate` with `args.isDelete === true`** - `return false` (React/Angular/JS/jQuery; Vue omits `onEventUpdate`); (3) **context menu "Delete"**. Use a `confirmOpen` flag to prevent double-triggering from (1) and (2).
- **Right-click context menu**: `onEventRightClick` → `args.domEvent.preventDefault()`, open an anchored `Select` popup with "Update" / "Delete" options. Imperative API: also store `selectedEvent = args.event` as a fallback - used in `deleteSelectedEvents` and `updateSelectedEvents` when `inst.getSelectedEvents()` returns an empty array.
- **Keyboard delete**: attach a `keydown` listener on the container; if Delete or Backspace is pressed and no confirm dialog is open, call `deleteSelectedEvents()`.

## Run it in your project

1. If not signed up yet, or don't own a license, you can [start a free trial](https://mobiscroll.com/starttrial).

2. Clone the repository:

   ```
   git clone https://github.com/acidb/mobiscroll-demos-react.git
   ```

3. If not installed yet, install the Mobiscroll CLI from NPM:

   ```
   npm install -g @mobiscroll/cli
   ```

4. Config Mobiscroll - open a terminal window in the root directory and run:

   ```
   mobiscroll config react
   ```

5. Install dependencies - open a terminal window in the root directory and run:

   ```
   npm install
   ```

6. Run the project:

   ```
   npm run dev
   ```

On a trial, add `--trial` to the config command: `mobiscroll config react --trial`.

Running a demo locally requires a Mobiscroll trial or a license; setup is per framework at https://mobiscroll.com/docs/react/getting-started/installation

## Related demos

- Previous: [Block Past Event Creation](https://demo.mobiscroll.com/react/eventcalendar/disallow-past-event-creation.md) - Keep past dates read-only in a React event calendar — block creation and editing on past days and show toast feedback on failed attempts.
- Next: [Cut, Copy, Paste Events](https://demo.mobiscroll.com/react/eventcalendar/cut-copy-paste-events-between-calendars.md) - Transfer events between two React event calendars with cut, copy, paste, and undo — using keyboard shortcuts or a right-click menu.
