---
title: "Vue Event calendar Block Past Event Creation Example | Mobiscroll"
component: "Event calendar"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/eventcalendar/disallow-past-event-creation"
---

# Block Past Event Creation - Vue Event calendar demo

Prevent creating or moving events to past dates in a Vue event calendar — disable past cells, lock past events read-only, and show toast feedback for blocked actions. For Vue and Nuxt.

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

## Description

Sometimes we don't want users to be able to create events in the past or to alter past events. This might be role based in some cases or applied in general to the calendar.

To disable past event creation and manipulation, a couple of things need to be handled:

- **Invalidate dates that are before today** - this takes care of validating event drop and event creation on past dates
- **Mark past events as fixed** - to do this, set the `editable` property of the event object to `false` for the events that should remain fixed

- **Handle past occurrences of recurring events** - recurring events are loaded as a single event and the occurrences are generated by the event calendar. Moving the past occurrences need to be handled in the [`event-update`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventUpdate) and [`event-create`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreate) lifecycle events
- **Give feedback to the user** - optionally, a toast can be displayed to give feedback to the user why an event cannot be dropped, moved and created in the past. For this we can use the [`event-update-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreateFailed) and [`event-create-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventUpdateFailed) lifecycle events

## What this demo shows

- A desktop month view event calendar with multiple events displayed directly in the month grid.
- **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. 
- **Past dates** Day cells before today are disabled and rendered with a gray background.
- **Disabled past days** Disabled past dates block event creation and drag and drop interactions on those cells.
- **Past events** Events on past dates remain visible, but they cannot be moved or repositioned.
- **Failed past updates** If a user tries to move a past recurring event, a toast appears at the bottom center with the message `Can't move past events.`
- **Overflow handling** The number of visible event labels depends on the available height in each day cell. Additional events are collapsed behind an `X more` link.
- **Popover** Clicking the `X more` link opens a popover that shows the hidden events for that day.
- **Label interaction** Hovering over or clicking an event label selects it and highlights the selected label.
- **Event creation** Users can create events on future dates by clicking and dragging across calendar cells or by double-clicking a day cell.
- **Hover on past days** Hovering a disabled past day shows a disabled icon.
- **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.
- **Future dates** Future day cells remain interactive and support new event creation.

## Best for

- **Booking and scheduling flows** Preventing users from creating or changing events in the past when only current and future availability should be managed.
- **Operational calendars** Keeping historical calendar data fixed for teams that use the month view for planning upcoming work, appointments, or activities.
- **Mixed-interaction month views** Allowing users to keep creating and editing future events while clearly separating past dates as read-only.

## API used in this demo

- [`theme`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-theme) - option
- [`themeVariant`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-themeVariant) - option
- [`labels`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-labels) - option
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-data) - option
- [`invalid`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-invalid) - option
- [`clickToCreate`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-clickToCreate) - option
- [`dragToCreate`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToCreate) - option
- [`dragToMove`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToMove) - option
- [`dragToResize`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-dragToResize) - option
- [`event-create-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreateFailed) - event
- [`event-update-failed`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventUpdateFailed) - event
- [`event-create`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventCreate) - event
- [`event-update`](https://mobiscroll.com/docs/vue/eventcalendar/api#event-onEventUpdate) - event
- [`close`](https://mobiscroll.com/docs/vue/notifications/toast#event-onClose) - event

## Code

### SFC JS

```vue
<script setup>
import {
  getJson,
  MbscEventcalendar,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

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

const today = new Date()
const myEvents = ref([])
const toastMessage = ref('')
const isToastOpen = ref(false)

const myView = {
  calendar: {
    labels: true
  }
}

const myInvalid = [
  {
    recurring: {
      repeat: 'daily',
      until: today
    }
  }
]

function handleEventCreateFailed(args) {
  if (!args.originEvent) {
    toastMessage.value = "Can't create event in the past"
    isToastOpen.value = true
  }
}

function handleEventUpdateFailed(args) {
  if (!args.oldEventOccurrence) {
    toastMessage.value = "Can't move event in the past"
    isToastOpen.value = true
  }
}

function handleEventCreate(args) {
  const oldEvent = args.originEvent
  const start = oldEvent && oldEvent.start ? oldEvent.start : null

  // Handle recurring events
  if (start && start < today) {
    toastMessage.value = "Can't move past event"
    isToastOpen.value = true
    return false
  }
}

function handleEventUpdate(args) {
  const oldEvent = args.oldEvent
  const start = oldEvent && oldEvent.start ? oldEvent.start : null
  const oldEventOccurrence = args.oldEventOccurrence
  const occurrenceStart =
    oldEventOccurrence && oldEventOccurrence.start ? oldEventOccurrence.start : null

  // Handle recurring events
  if ((start && start < today) || (occurrenceStart && occurrenceStart < today)) {
    return false
  }
}

function handleToastClose() {
  isToastOpen.value = false
}

onMounted(() => {
  getJson(
    'https://trial.mobiscroll.com/events/?vers=5',
    (events) => {
      for (const event of events) {
        // Convert dates to date objects
        event.start = event.start ? new Date(event.start) : event.start
        event.end = event.end ? new Date(event.end) : event.end
        // Mark past events as fixed by setting the event.editable property to false
        event.editable = event.start && today < event.start
      }
      myEvents.value = events
    },
    'jsonp'
  )
})
</script>

<template>
  <MbscEventcalendar
    :view="myView"
    :data="myEvents"
    :invalid="myInvalid"
    :clickToCreate="true"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    @event-create-failed="handleEventCreateFailed"
    @event-update-failed="handleEventUpdateFailed"
    @event-create="handleEventCreate"
    @event-update="handleEventUpdate"
    className="md-disallow-past-event-creation"
  />
  <MbscToast :message="toastMessage" :isOpen="isToastOpen" @close="handleToastClose" />
</template>

<style>
.md-disallow-past-event-creation .mbsc-readonly-event {
  opacity: 0.6;
}
</style>
```

### SFC TS

```vue
<script setup lang="ts">
import {
  getJson,
  MbscEventcalendar,
  MbscToast,
  setOptions
} from '@mobiscroll/vue'
import type {
  MbscCalendarEvent,
  MbscEventcalendarView,
  MbscEventCreateEvent,
  MbscEventCreateFailedEvent,
  MbscEventUpdateEvent,
  MbscEventUpdateFailedEvent
} from '@mobiscroll/vue'
import { onMounted, ref } from 'vue'

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

const today = new Date()
const myEvents = ref<MbscCalendarEvent[]>([])
const toastMessage = ref<string>('')
const isToastOpen = ref<boolean>(false)

const myView: MbscEventcalendarView = {
  calendar: {
    labels: true
  }
}

const myInvalid = [
  {
    recurring: {
      repeat: 'daily',
      until: today
    }
  }
]

function handleEventCreateFailed(args: MbscEventCreateFailedEvent) {
  if (!args.originEvent) {
    toastMessage.value = "Can't create event in the past"
    isToastOpen.value = true
  }
}

function handleEventUpdateFailed(args: MbscEventUpdateFailedEvent) {
  if (!args.oldEventOccurrence) {
    toastMessage.value = "Can't move event in the past"
    isToastOpen.value = true
  }
}

function handleEventCreate(args: MbscEventCreateEvent) {
  const oldEvent = args.originEvent
  const start = oldEvent && oldEvent.start ? oldEvent.start : null

  // Handle recurring events
  if (start && start < today) {
    toastMessage.value = "Can't move past event"
    isToastOpen.value = true
    return false
  }
}

function handleEventUpdate(args: MbscEventUpdateEvent) {
  const oldEvent = args.oldEvent
  const start = oldEvent && oldEvent.start ? oldEvent.start : null
  const oldEventOccurrence = args.oldEventOccurrence
  const occurrenceStart =
    oldEventOccurrence && oldEventOccurrence.start ? oldEventOccurrence.start : null

  // Handle recurring events
  if ((start && start < today) || (occurrenceStart && occurrenceStart < today)) {
    return false
  }
}

function handleToastClose() {
  isToastOpen.value = false
}

onMounted(() => {
  getJson(
    'https://trial.mobiscroll.com/events/?vers=5',
    (events) => {
      for (const event of events) {
        // Convert dates to date objects
        event.start = event.start ? new Date(event.start) : event.start
        event.end = event.end ? new Date(event.end) : event.end
        // Mark past events as fixed by setting the event.editable property to false
        event.editable = event.start && today < event.start
      }
      myEvents.value = events
    },
    'jsonp'
  )
})
</script>

<template>
  <MbscEventcalendar
    :view="myView"
    :data="myEvents"
    :invalid="myInvalid"
    :clickToCreate="true"
    :dragToCreate="true"
    :dragToMove="true"
    :dragToResize="true"
    @event-create-failed="handleEventCreateFailed"
    @event-update-failed="handleEventUpdateFailed"
    @event-create="handleEventCreate"
    @event-update="handleEventUpdate"
    className="md-disallow-past-event-creation"
  />
  <MbscToast :message="toastMessage" :isOpen="isToastOpen" @close="handleToastClose" />
</template>

<style>
.md-disallow-past-event-creation .mbsc-readonly-event {
  opacity: 0.6;
}
</style>
```

## Implementation notes

- Use `view: { calendar: { labels: true } }`. Enable all four interaction modes: `clickToCreate: true`, `dragToCreate: true`, `dragToMove: true`, `dragToResize: true`.
- Compute `today = new Date()` at module level. Pass `invalid: [{ recurring: { repeat: 'daily', until: today } }]` - this single entry blocks all dates up to and including today, preventing event creation and drop on past day cells.
- Load events via `getJson`. After loading, convert `event.start` and `event.end` to `Date` objects, then set `event.editable = event.start && today < event.start` - only future events are editable; past events get `editable: false`, which locks them against drag/resize/delete. JS/jQuery: call `inst.setEvents(events)`.
- **`onEventCreateFailed`** - show a toast (`"Can't create event in the past"`) only when `args.originEvent` is absent (guards against false positives from recurring events).
- **`onEventUpdateFailed`** - show a toast (`"Can't move event in the past"`) only when `args.oldEventOccurrence` is absent.
- **`onEventCreate`** - handles the recurring event edge case: if `args.originEvent?.start < today`, show a toast (`"Can't move past event"`) and `return false`. JS/jQuery: also call `inst.updateEvent(args.originEvent)` before returning false to restore the event.
- **`onEventUpdate`** - if `args.oldEvent.start < today` or `args.oldEventOccurrence?.start < today`, `return false`. JS/jQuery: also call `inst.updateEvent(args.oldEvent)` before returning false.

## 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-vue.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 vue
   ```

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 vue --trial`.

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

## Related demos

- Previous: [Recurring Events Editor](https://demo.mobiscroll.com/vue/eventcalendar/recurring-event-add-edit-dialog.md) - Add recurring event management to a Vue event calendar — use preset daily, weekly, monthly, and yearly rules or a custom recurrence editor.
- Next: [Bulk Actions](https://demo.mobiscroll.com/vue/eventcalendar/event-bulk-actions-edit-delete-update.md) - Multi-select events by keyboard in a Vue calendar - bulk delete, update, or run custom actions via action panel or right-click context menu.
