---
title: "Vue Timeline Custom Event Ordering Example | Mobiscroll"
component: "Timeline"
framework: "Vue"
library_version: "6.1.5"
url: "https://demo.mobiscroll.com/vue/timeline/custom-event-sort"
---

# Custom Event Ordering - Vue Timeline demo

Set event order in Vue Timeline rows via per-event order value or a custom ordering function — useful for priority-based and dense weekly schedules. For Vue and Nuxt.

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

## Description

The rendering engine uses the following two concepts to determine how the events are rendered:

1. Event data order
2. Event layout

The data order is determined by the following logic:

- All-day events are placed at the top
- Non-all-day events follow, sorted by their start times
- Events with the same start time are ordered alphabetically by their title

The event layout process determines the visual positioning and dimensions of events. This is a built-in functionality and cannot be altered externally. The layout algorithm processes the sorted event list and calculates each event's position and size. The algorithm follows these steps:

1. The first event is placed in the first position of the event track
2. If two or more events overlap in their start/end times, the later event is placed in the next event track, positioned below to the previous event
3. If a subsequent event does not overlap with any already added events, it is placed back in the first event track
4. This process continues until all events are positioned within their respective rows

The `order` property of the event data can be used to override the default ordering. The `order` property takes precedence over the default rules. If two events have the same order value, the default rules apply. For a more advanced order logic, the [`eventOrder` option](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-eventOrder) can be used which expects a function that compares two events and returns an order (-1 or 1).

**Do you want to learn about the event ordering?** [Learn more about it in the documentation →](https://mobiscroll.com/docs/vue/eventcalendar/timeline#event-order)

## What this demo shows

- A desktop weekly timeline for Monday through Friday, with days arranged horizontally and resources arranged vertically on the left.
- **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 weeks and jump back to the current day.
- **Week view** The fixed strip below the header shows the selected work week from Monday to Friday, with the current date highlighted.
- **Resources** Shows two resources vertically on the left side of the timeline as individual rows.
- **Event labels** Renders events as colored labels inside the day cells with event title shown in bold.
- **Custom event order** The demo shows how the event `order` property can override the default event ordering rules.
- **Default event order** Without a custom `order` value, all-day events are placed first, timed events are sorted by start time, and events with the same start time are sorted alphabetically by title.
- **Layout behavior** The built-in timeline layout algorithm places overlapping events into adjacent columns and returns non-overlapping events to the first available column.
- **Advanced ordering** The `eventOrder` option can be used when event ordering needs custom comparison logic beyond the `order` property.
- **Date positioning** Positions events according to both their assigned resource and exact date range.
- **Event interaction** Highlights events on hover and shows drag and resize handles for repositioning or changing duration.
- **Event selection** Selects and highlights an event when it is clicked.
- **Event creation** Creates a new event by double-clicking the timeline or by clicking and dragging on it.
- **Scrolling** The timeline supports horizontal and vertical scrolling for navigating through time and resources.

## Best for

- **Custom event ordering** Demonstrating how to control the order of overlapping events in a timeline view.
- **Dense weekly schedules** Showing multiple events that share the same day or time range while keeping their visual order predictable.
- **Priority-based scheduling** Displaying events where priority, status, or another business rule should influence render order.

## 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
- [`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
- [`view`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-view) - option
- [`data`](https://mobiscroll.com/docs/vue/eventcalendar/api#opt-data) - option

## Code

### SFC JS

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

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

const now = new Date()
const day = now.getDay()
const monday = now.getDate() - day + (day == 0 ? -6 : 1)
const myEvents = [
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'Kate OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'John OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'Mark OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'Emma OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 1),
    title: 'Mark OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 1),
    title: 'Carol OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Luke OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Carol OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Kate OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Dean OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Emma OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Jason OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 3),
    title: 'Jason OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 4),
    title: 'Ryan OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 4),
    title: 'John OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 4),
    title: 'Dean OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  }
]

const myView = {
  timeline: {
    type: 'week',
    eventDisplay: 'fill',
    startDay: 1,
    endDay: 5
  }
}
</script>

<template>
  <MbscEventcalendar :clickToCreate="true" :dragToCreate="true" :dragToMove="true" :dragToResize="true" :eventDelete="true" :view="myView" :data="myEvents" />
</template>
```

### SFC TS

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

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

const now = new Date()
const day = now.getDay()
const monday = now.getDate() - day + (day == 0 ? -6 : 1)

const myEvents: MbscCalendarEvent[] = [
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'Kate OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'John OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'Mark OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday),
    title: 'Emma OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 1),
    title: 'Mark OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 1),
    title: 'Carol OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Luke OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Carol OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Kate OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Dean OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Emma OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 2),
    title: 'Jason OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 3),
    title: 'Jason OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 4),
    title: 'Ryan OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 4),
    title: 'John OFF (APPROVED)',
    color: '#00ca10',
    allDay: true,
    accepted: true,
    order: 2
  },
  {
    start: new Date(now.getFullYear(), now.getMonth(), monday + 4),
    title: 'Dean OFF (PROPOSED)',
    color: '#e7b300',
    allDay: true,
    accepted: false,
    order: 1
  }
]

const myView: MbscEventcalendarView = {
  timeline: {
    type: 'week',
    eventDisplay: 'fill',
    startDay: 1,
    endDay: 5
  }
}
</script>

<template>
  <MbscEventcalendar :clickToCreate="true" :dragToCreate="true" :dragToMove="true" :dragToResize="true" :eventDelete="true" :view="myView" :data="myEvents" />
</template>
```

## Implementation notes

- Use `timeline: { type: 'week', eventDisplay: 'fill', startDay: 1, endDay: 5 }` - a Monday-Friday week with no resources. All events are `allDay: true`, so they render as full-width fill blocks grouped by day.
- Add a custom `order` property to each event. Lower values are rendered first (higher up within a day cell). Assign `order: 1` to PROPOSED events and `order: 2` to APPROVED events so pending requests always appear above approved ones, regardless of alphabetical title order.
- Color-code by status: yellow (`#e7b300`) for PROPOSED, green (`#00ca10`) for APPROVED. Optionally add a custom `accepted` boolean property as a semantic marker for use in other logic or renderers.
- Compute the current week's Monday from today: `monday = now.getDate() - now.getDay() + (now.getDay() === 0 ? -6 : 1)`. Offset each event's `start` by `monday + N` to distribute entries across Mon-Fri of the current week.
- No `eventOrder` comparator function is needed here - the per-event `order` property is sufficient. For more advanced sorting logic (e.g. sorting by a computed priority), use the `eventOrder` prop with a comparator function `(a, b) => ...` that returns `-1` or `1`.

## 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: [Custom Range Picker View](https://demo.mobiscroll.com/vue/timeline/custom-range-view.md) - Replace the Vue Timeline's default header navigation with a range picker — let users set any custom date range, synced two-way with the timeline.
- Next: [Equal Row Heights](https://demo.mobiscroll.com/vue/timeline/timeline-resource-height.md) - Keep all resource rows the same height on Vue Timeline — overlapping events distribute evenly within uniform rows, with CSS override for min-height.
