---
title: "Vue Scheduler Event Rendering Example | Mobiscroll"
component: "Scheduler"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/scheduler/customizing-events"
---

# Event Rendering - Vue Scheduler demo

Render custom event cards in the Vue Scheduler — add color stripes, labels, avatars, action buttons, and travel-time buffers. For Vue and Nuxt.

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

## Description

Above the built in rendering mode you can either customize the full event or just the content of the event. If we break up the event into pieces there are six fields that we are interested in:

- The event `start` and `end` time
- Whether it is an `allDay` event or not
- The event `color`
- The event `title`
- `bufferBefore`
- `bufferAfter`

The scheduler takes care of positioning the event container and everything else is your responsibility. You will have to place and provide styling to all event fields. Beside the ones mentioned above you'll be able to render other custom fields, like `description`, `location`, `participants` ... and add buttons, custom interactions.

Pass the event template to the [`schedulerEvent`](https://mobiscroll.com/docs/vue/eventcalendar/api#slot-schedulerEvent) template. All original event fields along with computed fields like `isMultiDay`, `lastDay` will be available inside the template.

The before/after buffers can help you visualise delays or added minutes for tasks. For example travel time for meetings/appointments, check in/check out for flights.

Templating the buffers can be done through the [`bufferBefore`](https://mobiscroll.com/docs/vue/eventcalendar/api#slot-bufferBefore) and [`bufferAfter`](https://mobiscroll.com/docs/vue/eventcalendar/api#slot-bufferAfter) options.

## What this demo shows

- A desktop daily scheduler layout with a fixed week strip at the top, a fixed all-day event row below it, and a scrollable time grid for the selected day.
- **Header navigation** The month and year label in the top left opens date navigation, while the blue previous and next arrows and the Today button on the right make it easy to move between dates and jump back to the current day.
- **Week view** The fixed week strip below the header shows the surrounding dates for quick day switching. Dates highlight on hover, and the selected day is marked with a blue circle.
- **All-day events** All-day events are displayed in a dedicated row that stays fixed above the time grid.
- **Time grid** The scheduler below the all-day row scrolls vertically through the hours of the selected day.
- **Hover feedback** Hovering the time grid shows a time indicator that follows the pointer in 15-minute increments.
- **Custom event rendering** Timed events are rendered as colored cards with a colored left stripe, a project label above the title, a bold event title, the exact start and end time, an Edit button, and participant avatars in the bottom-left corner.
- **Edit interaction** Clicking the Edit button shows a toast at the bottom center of the scheduler with the text "Edit clicked".
- **Buffer rendering** The custom buffer area appears with event color and white diagonal stripe background and a centered travel-time label showing "Travel time" and "60 minutes", with the duration emphasized in bold.
- **Event overlapping** If events overlap, the scheduler places them side by side without hiding any of them or causing other conflicts.
- **Event interactions** Hovering an event highlights it and shows resize and drag handles, indicating that events can be resized or repositioned.
- **Event creation** Double-clicking the time grid or clicking and dragging on it creates a new event.
- **Current time** A blue line across the scheduler indicates the current time.

## Best for

- **Custom scheduler event cards** Scheduler views that need event cards to match a product-specific visual style instead of using the default event rendering.
- **Project or category-based scheduling** Schedulers where color, labels, and visual accents help users distinguish projects, categories, teams, or statuses.
- **Detailed event summaries** Scheduling interfaces that need to show additional context inside each event, such as time ranges, custom fields, actions, and participants.
- **Travel or preparation buffers** Workflows where time before an event needs to be visualized separately, such as travel time, setup time, or check-in time.

## 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
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`labels`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-labels) - 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
- [`eventDelete`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-eventDelete) - option
- [`data`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-data) - option
- [`responsive`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-responsive) - option
- [`close`](https://mobiscroll.com/docs/vue/notifications/toast#event-onClose) - event

## Code

### SFC JS

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

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

const myEvents = ref([])

const myResponsive = ref({
  xsmall: {
    view: {
      scheduler: {
        type: 'day'
      }
    }
  },
  medium: {
    view: {
      scheduler: {
        type: 'week'
      }
    }
  }
})

const isToastOpen = ref(false)

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

function handleToastClose() {
  isToastOpen.value = false
}

function edit() {
  isToastOpen.value = true
}

function getCategory(id) {
  switch (id) {
    case 1:
      return {
        name: 'Project X',
        color: '#ff825d'
      }
    case 2:
      return {
        name: 'Stakeholder Mtg.',
        color: '#bd75d0'
      }
    case 3:
      return {
        name: 'Status Update',
        color: '#7f9230'
      }
    case 4:
      return {
        name: 'Information Sharing',
        color: '#f14590'
      }
    case 5:
      return {
        name: 'Team Building',
        color: '#64cad4'
      }
    default:
      return {
        name: 'No category',
        color: '#5ac8fa'
      }
  }
}

function getParticipant(id) {
  switch (id) {
    case 1:
      return {
        name: 'Lisa',
        img: 'https://img.mobiscroll.com/demos/f1.png'
      }
    case 2:
      return {
        name: 'Sharon',
        img: 'https://img.mobiscroll.com/demos/f2.png'
      }
    case 3:
      return {
        name: 'Emily',
        img: 'https://img.mobiscroll.com/demos/f3.png'
      }
    case 4:
      return {
        name: 'Rose',
        img: 'https://img.mobiscroll.com/demos/f4.png'
      }
    case 5:
      return {
        name: 'Matt',
        img: 'https://img.mobiscroll.com/demos/m1.png'
      }
    case 6:
      return {
        name: 'Rick',
        img: 'https://img.mobiscroll.com/demos/m2.png'
      }
    case 7:
      return {
        name: 'John',
        img: 'https://img.mobiscroll.com/demos/m3.png'
      }
    case 8:
      return {
        name: 'Ethan',
        img: 'https://img.mobiscroll.com/demos/m4.png'
      }
  }
}

onMounted(() => {
  getJson(
    'https://trial.mobiscroll.com/multi-events/',
    (events) => {
      myEvents.value = events
    },
    'jsonp'
  )
})
</script>

<template>
  <MbscEventcalendar :clickToCreate="true" :dragToCreate="true" :dragToMove="true" :dragToResize="true" :eventDelete="true"
    :view="myView"
    :data="myEvents"
    :responsive="myResponsive"
    className="md-custom-event"
  >
    <template #schedulerEvent="data">
      <div
        v-if="data.allDay"
        :style="{ background: getCategory(data.original.category).color }"
        class="md-custom-event-allday-title"
      >
        {{ data.title }}
      </div>
      <div
        v-if="!data.allDay"
        class="md-custom-event-cont"
        :set="(cat = getCategory(data.original.category))"
        :style="{
          borderLeft: '5px solid ' + cat.color,
          background: cat.color
        }"
      >
        <div class="md-custom-event-wrapper">
          <div :style="{ background: cat.color }" class="md-custom-event-category">
            {{ cat.name }}
          </div>
          <div class="md-custom-event-details">
            <div class="md-custom-event-title">{{ data.title }}</div>
            <div class="md-custom-event-time">{{ data.start }} - {{ data.end }}</div>
            <MbscButton className="md-custom-event-btn" color="dark" variant="outline" @click="edit"
              >Edit</MbscButton
            >
            <div class="md-cutom-event-img-cont">
              <img
                v-for="p in data.original.participants"
                :key="p.name"
                class="md-custom-event-img"
                :src="getParticipant(p).img"
              />
            </div>
          </div>
        </div>
      </div>
    </template>
    <template #bufferBefore="data">
      <div class="md-schedule-buffer md-schedule-before-buffer">
        <div
          class="md-schedule-buffer-background"
          :style="{
            background: `repeating-linear-gradient(-45deg,#fcfffc,#fcfffc 10px,${
              getCategory(data.original.category).color
            } 10px,${getCategory(data.original.category).color} 20px)`
          }"
        ></div>
        <span class="md-buffer-text">Travel time </span
        ><span class="md-buffer-time">{{ data.original.bufferBefore }} minutes </span>
      </div>
    </template>
  </MbscEventcalendar>
  <MbscToast message="Edit clicked" :isOpen="isToastOpen" @close="handleToastClose" />
</template>

<style>
.md-custom-event-cont {
  font-size: 12px;
  font-weight: 600;
  height: 100%;
  background: #fff;
  position: relative;
  box-sizing: border-box;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
  border-radius: 0 10px 10px 0;
  overflow: hidden;
}

.md-custom-event-wrapper {
  position: relative;
  background: rgba(255, 255, 255, 0.5);
  height: 100%;
  min-height: 135px;
  box-sizing: border-box;
  border-radius: 0 10px 10px 0;
  transition: background 0.15s ease-in-out;
}

.mbsc-schedule-event-hover .md-custom-event-cont .md-custom-event-wrapper {
  background: rgba(255, 255, 255, 0.3);
}

.mbsc-schedule-event-active .md-custom-event-cont .md-custom-event-wrapper {
  box-shadow:
    0 4px 8px 0 rgba(0, 0, 0, 0.14),
    0 1px 8px 0 rgba(0, 0, 0, 0.12),
    0 1px 3px -1px rgba(0, 0, 0, 0.2);
}

.mbsc-schedule-event-hover .md-custom-event-allday-title {
  opacity: 0.8;
}

.mbsc-schedule-event-active .md-custom-event-allday-title {
  box-shadow:
    0 4px 8px 0 rgba(0, 0, 0, 0.14),
    0 1px 8px 0 rgba(0, 0, 0, 0.12),
    0 1px 3px -1px rgba(0, 0, 0, 0.2);
}

.md-custom-event-category {
  display: inline-block;
  max-width: 100%;
  color: #eee;
  border-radius: 10px;
  margin: 10px;
  padding: 2px 10px;
  white-space: nowrap;
  overflow: hidden;
}

.md-custom-event-details {
  padding: 0 10px;
}

.md-custom-event-title {
  color: initial;
}

.md-custom-event-time {
  font-size: 10px;
  color: #666;
  padding-top: 3px;
}

.md-custom-event-allday-title {
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  padding: 2px 10px;
  border-radius: 10px;
  opacity: 0.7;
}

.md-custom-event-btn.mbsc-button {
  margin: 5px 0;
  line-height: 20px;
  padding-top: 0;
  padding-bottom: 0;
}

.md-cutom-event-img-cont {
  position: absolute;
  bottom: 5px;
  left: 10px;
  white-space: nowrap;
}

.md-custom-event-img {
  width: 25px;
  height: 25px;
  margin: 0 2px;
}

.md-schedule-buffer {
  position: absolute;
  display: flex;
  height: 100%;
  font-size: 10px;
  left: 0;
  right: 0;
  color: #000;
  padding: 7px 4px;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  overflow: hidden;
}

.md-schedule-buffer-background {
  position: absolute;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  z-index: -1;
}

.md-schedule-before-buffer {
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
}

.md-buffer-time,
.md-buffer-text {
  width: 50px;
  padding: 0 3px;
  text-align: center;
}

.md-buffer-time {
  font-weight: 600;
}
</style>
```

### SFC TS

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

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

let cat: any

const myEvents = ref<MbscCalendarEvent[]>([])

const myResponsive: any = ref({
  xsmall: {
    view: {
      scheduler: {
        type: 'day'
      }
    }
  },
  medium: {
    view: {
      scheduler: {
        type: 'week'
      }
    }
  }
})

const isToastOpen = ref<boolean>(false)

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

function handleToastClose() {
  isToastOpen.value = false
}

function edit() {
  isToastOpen.value = true
}

function getCategory(id: number) {
  switch (id) {
    case 1:
      return {
        name: 'Project X',
        color: '#ff825d'
      }
    case 2:
      return {
        name: 'Stakeholder Mtg.',
        color: '#bd75d0'
      }
    case 3:
      return {
        name: 'Status Update',
        color: '#7f9230'
      }
    case 4:
      return {
        name: 'Information Sharing',
        color: '#f14590'
      }
    case 5:
      return {
        name: 'Team Building',
        color: '#64cad4'
      }
    default:
      return {
        name: 'No category',
        color: '#5ac8fa'
      }
  }
}

function getParticipant(id: number) {
  switch (id) {
    case 1:
      return {
        name: 'Lisa',
        img: 'https://img.mobiscroll.com/demos/f1.png'
      }
    case 2:
      return {
        name: 'Sharon',
        img: 'https://img.mobiscroll.com/demos/f2.png'
      }
    case 3:
      return {
        name: 'Emily',
        img: 'https://img.mobiscroll.com/demos/f3.png'
      }
    case 4:
      return {
        name: 'Rose',
        img: 'https://img.mobiscroll.com/demos/f4.png'
      }
    case 5:
      return {
        name: 'Matt',
        img: 'https://img.mobiscroll.com/demos/m1.png'
      }
    case 6:
      return {
        name: 'Rick',
        img: 'https://img.mobiscroll.com/demos/m2.png'
      }
    case 7:
      return {
        name: 'John',
        img: 'https://img.mobiscroll.com/demos/m3.png'
      }
    case 8:
      return {
        name: 'Ethan',
        img: 'https://img.mobiscroll.com/demos/m4.png'
      }
  }
}

onMounted(() => {
  getJson(
    'https://trial.mobiscroll.com/multi-events/',
    (events) => {
      myEvents.value = events
    },
    'jsonp'
  )
})
</script>

<template>
  <MbscEventcalendar :clickToCreate="true" :dragToCreate="true" :dragToMove="true" :dragToResize="true" :eventDelete="true"
    :view="myView"
    :data="myEvents"
    :responsive="myResponsive"
    className="md-custom-event"
  >
    <template #schedulerEvent="data">
      <div
        v-if="data.allDay"
        :style="{ background: getCategory(data.original.category).color }"
        class="md-custom-event-allday-title"
      >
        {{ data.title }}
      </div>
      <div
        v-if="!data.allDay"
        class="md-custom-event-cont"
        :set="(cat = getCategory(data.original.category))"
        :style="{
          borderLeft: '5px solid ' + cat.color,
          background: cat.color
        }"
      >
        <div class="md-custom-event-wrapper">
          <div :style="{ background: cat.color }" class="md-custom-event-category">
            {{ cat.name }}
          </div>
          <div class="md-custom-event-details">
            <div class="md-custom-event-title">{{ data.title }}</div>
            <div class="md-custom-event-time">{{ data.start }} - {{ data.end }}</div>
            <MbscButton className="md-custom-event-btn" color="dark" variant="outline" @click="edit"
              >Edit</MbscButton
            >
            <div class="md-cutom-event-img-cont">
              <img
                v-for="p in data.original.participants"
                :key="p.name"
                class="md-custom-event-img"
                :src="getParticipant(p).img"
              />
            </div>
          </div>
        </div>
      </div>
    </template>
    <template #bufferBefore="data">
      <div class="md-schedule-buffer md-schedule-before-buffer">
        <div
          class="md-schedule-buffer-background"
          :style="{
            background: `repeating-linear-gradient(-45deg,#fcfffc,#fcfffc 10px,${
              getCategory(data.original.category).color
            } 10px,${getCategory(data.original.category).color} 20px)`
          }"
        ></div>
        <span class="md-buffer-text">Travel time </span
        ><span class="md-buffer-time">{{ data.original.bufferBefore }} minutes </span>
      </div>
    </template>
  </MbscEventcalendar>
  <MbscToast message="Edit clicked" :isOpen="isToastOpen" @close="handleToastClose" />
</template>

<style>
.md-custom-event-cont {
  font-size: 12px;
  font-weight: 600;
  height: 100%;
  background: #fff;
  position: relative;
  box-sizing: border-box;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
  border-radius: 0 10px 10px 0;
  overflow: hidden;
}

.md-custom-event-wrapper {
  position: relative;
  background: rgba(255, 255, 255, 0.5);
  height: 100%;
  min-height: 135px;
  box-sizing: border-box;
  border-radius: 0 10px 10px 0;
  transition: background 0.15s ease-in-out;
}

.mbsc-schedule-event-hover .md-custom-event-cont .md-custom-event-wrapper {
  background: rgba(255, 255, 255, 0.3);
}

.mbsc-schedule-event-active .md-custom-event-cont .md-custom-event-wrapper {
  box-shadow:
    0 4px 8px 0 rgba(0, 0, 0, 0.14),
    0 1px 8px 0 rgba(0, 0, 0, 0.12),
    0 1px 3px -1px rgba(0, 0, 0, 0.2);
}

.mbsc-schedule-event-hover .md-custom-event-allday-title {
  opacity: 0.8;
}

.mbsc-schedule-event-active .md-custom-event-allday-title {
  box-shadow:
    0 4px 8px 0 rgba(0, 0, 0, 0.14),
    0 1px 8px 0 rgba(0, 0, 0, 0.12),
    0 1px 3px -1px rgba(0, 0, 0, 0.2);
}

.md-custom-event-category {
  display: inline-block;
  max-width: 100%;
  color: #eee;
  border-radius: 10px;
  margin: 10px;
  padding: 2px 10px;
  white-space: nowrap;
  overflow: hidden;
}

.md-custom-event-details {
  padding: 0 10px;
}

.md-custom-event-title {
  color: initial;
}

.md-custom-event-time {
  font-size: 10px;
  color: #666;
  padding-top: 3px;
}

.md-custom-event-allday-title {
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  padding: 2px 10px;
  border-radius: 10px;
  opacity: 0.7;
}

.md-custom-event-btn.mbsc-button {
  margin: 5px 0;
  line-height: 20px;
  padding-top: 0;
  padding-bottom: 0;
}

.md-cutom-event-img-cont {
  position: absolute;
  bottom: 5px;
  left: 10px;
  white-space: nowrap;
}

.md-custom-event-img {
  width: 25px;
  height: 25px;
  margin: 0 2px;
}

.md-schedule-buffer {
  position: absolute;
  display: flex;
  height: 100%;
  font-size: 10px;
  left: 0;
  right: 0;
  color: #000;
  padding: 7px 4px;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  overflow: hidden;
}

.md-schedule-buffer-background {
  position: absolute;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  z-index: -1;
}

.md-schedule-before-buffer {
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
}

.md-buffer-time,
.md-buffer-text {
  width: 50px;
  padding: 0 3px;
  text-align: center;
}

.md-buffer-time {
  font-weight: 600;
}
</style>
```

## Implementation notes

- Use a `responsive` config: `xsmall: { view: { scheduler: { type: 'day' } } }`, `medium: { view: { scheduler: { type: 'week' } } }`. Load events from a JSONP endpoint on mount. React/Vue: call `getJson(url, callback, 'jsonp')`; JS: call `mobiscroll.getJson(url, callback, 'jsonp')`; jQuery: call `$.getJSON(url + '&callback=?', callback)`; Angular: `HttpClient.jsonp()`. React/Angular/Vue: in the callback, assign the fetched array to the state/property bound to the `data` prop. Imperative API (JS/jQuery): call `inst.setEvents(events)` in the callback.
- Events carry two custom properties: `category` (integer 1-5, maps to a name and color via a local lookup) and `participants` (array of integer IDs, each mapping to a person's avatar image URL). Some events also have `bufferBefore` (travel time in minutes).
- Pass a custom event renderer to `renderSchedulerEvent` (Vue: `#schedulerEvent` slot; Angular: `[schedulerEventTemplate]`). The renderer receives a `data` object - branch on `data.allDay`:
  - **All-day**: render a single colored div with `data.title`.
  - **Timed**: render a card with a colored left accent, a category badge (lookup name and color from `data.original.category`), `data.title`, the pre-formatted `data.start`-`data.end` time range, an "Edit" button, and participant avatar images mapped from `data.original.participants`.
- Pass a buffer renderer to `renderBufferBefore` (Vue: `#bufferBefore` slot; Angular: `[bufferBeforeTemplate]`). Render a diagonal stripe background using the category color, a "Travel time" label, and `args.original.bufferBefore` minutes.
- The Edit button click shows a `Toast` with "Edit clicked". Attach a click handler directly to the button element; JS/jQuery instead register `onEventClick` and check `args.domEvent.target` to identify whether the Edit button was clicked.

## 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: [Event Tooltip](https://demo.mobiscroll.com/vue/scheduler/custom-event-tooltip.md) - Show native, custom, or third-party tooltips on Vue Scheduler event hover — with appointment details and inline actions.
- Next: [Custom Date Header](https://demo.mobiscroll.com/vue/scheduler/date-header-template.md) - Customize the date headers of the Vue Scheduler — add custom layouts, labels, and milestone badges to day columns.
