Vue Cal
v. 4.10.2
Vue.js full cal  --no-deps --no-bs  :metal:

Go for the date picker...
or unleash the full potential!
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
4
25
3
26
2
27
3
28
3
29
30
31
1
2
3
4
5
6
Selected date: '2025-03-28'
 24
John
Kate
 25
John
Kate
 26
John
Kate
 27
John
Kate
 28
John
Kate
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
LUNCH
12:00 - 13:00
Tennis
15:30 - 17:30
sports_tennis
LUNCH
12:00 - 13:00
Tennis
15:30 - 17:30
sports_tennis
LUNCH
12:00 - 13:00
Volleyball
08:00 - 10:00
sports_volleyball
LUNCH
12:00 - 13:00
LUNCH
12:00 - 13:00
LUNCH
12:00 - 13:00
Golf
09:00 - 11:30
golf_course
LUNCH
12:00 - 13:00
LUNCH
12:00 - 13:00
LUNCH
12:00 - 13:00
LUNCH
12:00 - 13:00
Movie
16:45 - 18:45
local_play
View this example source code

Github project  &  important notes

  • Vue Cal v5 is cooking!
  • NEW: You can start discussions in this channel
  • Always check the Release Notes when you upgrade
  • Philosophy: separate logic & styles
    For performance, simplified logic, readability, structure & maintenance of your own code, what can be done via CSS should be done via CSS. CSS classes will help you style your events, cells, splits, etc.
    Vue Cal provides the minimum needed CSS styles, not the nicest one: that makes it highly customizable for all the scenarii and a breeze to override.
    You most likely don't need the !important flag.

Vue 2 has reached EOL, and so has Vue Cal legacy branch.
You can still use it for Vue 2 projects with npm i vue-cal@legacy, but it will not receive further updates or support.
Time to move on! 🚀

If you like Vue Cal, you canSponsor meorbuy me a coffee!
Thank you so much to all the backers! 🙏
Check out my Vue UI framework!Wave UI

To do...

Backlog
Recurring eventsDrag & drop multiple day eventsDrag & drop events on touch devicesFull Typescript supportFull SSR supportImprove multiple day events Month viewPrevent event intersect. on drag/resizeHorizontal timeline

Installation

You have 2 options: using NPM or <script> tag.

  • Via NPM

    npm i vue-cal # Vue 3
    npm i vue-cal # Vue 3
    or
    npm i vue-cal@legacy # Vue 2
    npm i vue-cal@legacy # Vue 2

    Then import Vue Cal in your Vue component and use it.

    import VueCal from 'vue-cal'
    import 'vue-cal/dist/vuecal.css'
    
    export default {
      components: { VueCal },
      ...
    }
    
    import VueCal from 'vue-cal' import 'vue-cal/dist/vuecal.css' export default { components: { VueCal }, ... }
  • Or via <script> tag

    Include the Vue Cal script in your document <head> as follows:

    <head>
      ...
      <script src="https://unpkg.com/vue"></script>
      <script src="https://unpkg.com/vue-cal"></script>
      <link href="https://unpkg.com/vue-cal/dist/vuecal.css" rel="stylesheet">
    </head>
    
    <head> ... <script src="https://unpkg.com/vue"></script> <script src="https://unpkg.com/vue-cal"></script> <link href="https://unpkg.com/vue-cal/dist/vuecal.css" rel="stylesheet"> </head>

    Then define the component to use in your template if you are not using the Vue composition API:

    // In your Vue.js component.
    export default {
      components: { VueCal: vuecal },
      ...
    }
    // In your Vue.js component. export default { components: { VueCal: vuecal }, ... }
    Demos on Codepen

    Check the different setups for direct browser use

How to use

Wait! Before you dive in, make sure you place Vue Cal in a container that has a set height! (not auto or initial)
By default Vue Cal will take the full width & height of its container if it has a set height.

<!-- If the container has no height, set a height on vue-cal -->
<vue-cal style="height: 250px" />
<!-- If the container has no height, set a height on vue-cal --> <vue-cal style="height: 250px" />
 24
 25
 26
 27
 28
 29
 30

Examples

In addition to all the examples below, you can try these common use cases on Codepen.

If you have a doubt about the setup, check back the examples of theinstallation section.

Basic

# Basic with no timeline & hidden weekends

Double click cell to go to a narrower view and click the title to go to a broader view.
By default the calendar theme is grey to match with most of web pages.
You can easily change the color theme (learn how): try this

 24
 25
 26
 27
 28
<vue-cal :time="false" hide-weekends />
<vue-cal :time="false" hide-weekends />

For all the options details, refer to the API section.

# Small calendar, no time, hidden view selector & custom arrows

Extra-small, no timeline, hidden view selector & custom arrows (using the reserved slots arrow-prev & arrow-next).
With a hidden view selector, you can still navigate between the different views: double click cell to go to a narrower view, click title to go to a broader view.

24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
<vue-cal
  hide-view-selector
  :time="false"
  active-view="month"
  xsmall>
  <template #arrow-prev>
    <i class="icon material-icons">arrow_back</i>
  </template>
  <template #arrow-next>
    <i class="icon material-icons">arrow_forward</i>
  </template>
</vue-cal>
<vue-cal hide-view-selector :time="false" active-view="month" xsmall> <template #arrow-prev> <i class="icon material-icons">arrow_back</i> </template> <template #arrow-next> <i class="icon material-icons">arrow_forward</i> </template> </vue-cal>

For all the options details, refer to the API section.

# Calendar themes - Rounded cells & date picker

You can easily change the calendar color theme or use the rounded-cells theme by applying the corresponding CSS class on the <vuecal> tag.
E.g. vuecal--rounded-theme, vuecal--green-theme, vuecal--blue-theme.
Read more about calendar themes in the CSS Notes section.

Because Vue Cal has the potential out of the box, you can also use it as a date picker.
Apply the css class vuecal--date-picker to have the date picker layout below.
you can also disable the transitions to have a faster effect.

24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
Date picker layout, no transition
<vue-cal
    class="vuecal--rounded-theme vuecal--green-theme"
    xsmall
    hide-view-selector
    :time="false"
    active-view="month"
    :disable-views="['week']"
    style="width: 270px;height: 300px">
</vue-cal>
<vue-cal class="vuecal--rounded-theme vuecal--green-theme" xsmall hide-view-selector :time="false" active-view="month" :disable-views="['week']" style="width: 270px;height: 300px"> </vue-cal>
<vue-cal
    class="vuecal--date-picker"
    xsmall
    hide-view-selector
    :time="false"
    :transitions="false"
    active-view="month"
    :disable-views="['week']"
    style="width: 210px;height: 230px">
</vue-cal>
<vue-cal class="vuecal--date-picker" xsmall hide-view-selector :time="false" :transitions="false" active-view="month" :disable-views="['week']" style="width: 210px;height: 230px"> </vue-cal>

Refer to the API section to read more about all the options.

# Disable views, active view

To hide views, you can use the disable-views option and provide an array of views to disable.
The views are not only hidden from the menu bar, they are totally disabled, even when navigating from cells and title bar clicks.

By default all the views are visible and the default active view is the week view.

2025
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
<vue-cal :time="false" active-view="month" :disable-views="['years', 'year', 'week']" />
<vue-cal :time="false" active-view="month" :disable-views="['years', 'year', 'week']" />

# Minimum / maximum dates & single click to navigate

With the options minDate & maxDate, you can set a time range of selectable cells. All the cells before and after are still visible but will be disabled and not selectable.
You can still navigate through them with arrows.
In this example, the minimum date is set to 10 days behind and the maximum date to 10 days ahead.

Notes

  • the min and max options accept a formatted string or plain Javascript Date object.
  • 2 different CSS class are available on out of range cells: .before-min & .after-max.

24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
<vue-cal
  xsmall
  hide-view-selector
  click-to-navigate
  :time="false"
  active-view="month"
  :min-date="minDate"
  :max-date="maxDate">
</vue-cal>
<vue-cal xsmall hide-view-selector click-to-navigate :time="false" active-view="month" :min-date="minDate" :max-date="maxDate"> </vue-cal>
// Using Vue Cal Date Prototypes (activated by default).
computed: {
  minDate () {
    return new Date().subtractDays(10)
  },
  maxDate () {
    return new Date().addDays(10)
  }
}
// Using Vue Cal Date Prototypes (activated by default). computed: { minDate () { return new Date().subtractDays(10) }, maxDate () { return new Date().addDays(10) } }
.vuecal__cell--disabled {text-decoration: line-through;}
.vuecal__cell--before-min {color: #b6d6c7;}
.vuecal__cell--after-max {color: #008b8b;}
.vuecal__cell--disabled {text-decoration: line-through;} .vuecal__cell--before-min {color: #b6d6c7;} .vuecal__cell--after-max {color: #008b8b;}

For all the options details, refer to the API section.

# Disable days

You can use the disable-days option to provide an array of formatted dates (e.g. 2020-09-18) to disable.

24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
<!-- Using Vue Cal Date Prototypes (activated by default): subtractDays, format, addDays -->
<vue-cal
  xsmall
  hide-view-selector
  click-to-navigate
  :time="false"
  active-view="month"
  :disable-views="['week']"
  :disable-days="[
    new Date().subtractDays(2).format(),
    new Date().format(),
    new Date().addDays(2).format()
  ]"
></vue-cal>
<!-- Using Vue Cal Date Prototypes (activated by default): subtractDays, format, addDays --> <vue-cal xsmall hide-view-selector click-to-navigate :time="false" active-view="month" :disable-views="['week']" :disable-days="[ new Date().subtractDays(2).format(), new Date().format(), new Date().addDays(2).format() ]" ></vue-cal>
.vuecal__cell--disabled {text-decoration: line-through;color: #bbb;}
.vuecal__cell--disabled {text-decoration: line-through;color: #bbb;}

For all the options details, refer to the API section.

# Hide particular week days & show the weeks numbers

If you want to hide particular days of the week, you can use the hide-weekdays option.
It accepts an array of days to hide (day numbers), starting at 1 for Monday, to 7 for Sunday.
This option will apply on month & week views.

If you want to hide Saturday and Sunday you can put `6, 7` in the array or use hide-weekends in supplement of hide-weekdays.

You can show the weeks numbers column on the month view with the show-week-numbers option.
You can also provide a custom renderer to the weeks numbers cells through the week-number-cell slot.

Refer to the API section to read more about all the options.

 24
 27
 29
 30
<vue-cal :time="false"
         show-week-numbers
         :hide-weekdays="[2, 3, 5]"
         :disable-views="['years', 'year']">
</vue-cal>
<vue-cal :time="false" show-week-numbers :hide-weekdays="[2, 3, 5]" :disable-views="['years', 'year']"> </vue-cal>

Internationalization (i18n)

# Internationalization

Current language:

Let you translate the calendar texts into your own language (locale).
Refer to the locale option in the API section to know more or if you want to provide a translation.
Try it in Codepen: Vue Cal - Internationalization.

<vue-cal :time="false" small active-view="year" locale="zh-cn" />
<vue-cal :time="false" small active-view="year" locale="zh-cn" />

For Vue Cal versions that don't support ESM (prior 4.3.4 on Vue 3 or 3.11.0 on Vue 2), the locale file must be loaded separately:
import 'vue-cal/dist/i18n/zh-cn.js'.

Alternative

If you need full control on the texts, you can alternatively provide an object containing all the texts (start from the locale JSON file matching your language).
Keep in mind this is not the recommended way: texts may be added / modified / removed in the library and your provided custom texts may not work anymore.
Always prefer the standard locales!

Timeline, business hours & Today

# Timeline

Timelines are only visible on week and day views.
This example has a set time range from 08:00 to 19:00, time step of 30 minutes (1 hour by default), 24-hour format, and hidden weekends.

 24
 25
 26
 27
 28
08:00
08:30
09:00
09:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
18:30
<!-- Time-start time-end & time-step are expected in minutes. -->
<vue-cal
  :time-from="8 * 60"
  :time-to="19 * 60"
  :time-step="30"
  hide-weekends>
</vue-cal>
<!-- Time-start time-end & time-step are expected in minutes. --> <vue-cal :time-from="8 * 60" :time-to="19 * 60" :time-step="30" hide-weekends> </vue-cal>

For all the options details, refer to the API section.

# Showing time labels in cells

You can choose to display the time labels in every cells by enabling the showTimeInCells option.

 24
 25
 26
 27
 28
08:00
08:30
09:00
09:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
18:30
08:0008:3009:0009:3010:0010:3011:0011:3012:0012:3013:0013:3014:0014:3015:0015:3016:0016:3017:0017:3018:0018:30
 
08:0008:3009:0009:3010:0010:3011:0011:3012:0012:3013:0013:3014:0014:3015:0015:3016:0016:3017:0017:3018:0018:30
 
08:0008:3009:0009:3010:0010:3011:0011:3012:0012:3013:0013:3014:0014:3015:0015:3016:0016:3017:0017:3018:0018:30
 
08:0008:3009:0009:3010:0010:3011:0011:3012:0012:3013:0013:3014:0014:3015:0015:3016:0016:3017:0017:3018:0018:30
 
08:0008:3009:0009:3010:0010:3011:0011:3012:0012:3013:0013:3014:0014:3015:0015:3016:0016:3017:0017:3018:0018:30
 
<vue-cal
  :time-from="8 * 60"
  :time-to="19 * 60"
  :time-step="30"
  hide-weekends
  show-time-in-cells>
</vue-cal>
<vue-cal :time-from="8 * 60" :time-to="19 * 60" :time-step="30" hide-weekends show-time-in-cells> </vue-cal>

For all the options details, refer to the API section.

# Special hours (or business hours)

The special hours are visible on week and day views and allow you to highlight a particular time range on each day of the week individually.

Refer to the API section to read more about the special-hours option.

13 ( 2025)
 24
 25
 26
 27
 28
 29
 30
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
<vue-cal
  :disable-views="['years', 'year', 'month']"
  :time-from="8 * 60"
  :time-to="20 * 60"
  :special-hours="specialHours" />
<vue-cal :disable-views="['years', 'year', 'month']" :time-from="8 * 60" :time-to="20 * 60" :special-hours="specialHours" />
// `from` and `to` are expected in minutes.
const dailyHours = { from: 9 * 60, to: 18 * 60, class: 'business-hours' }

// In your component's data, special hours from Monday to Friday.
// Note that you can provide an array of multiple blocks for the same day.
specialHours: {
  1: dailyHours,
  2: dailyHours,
  3: [
    { from: 9 * 60, to: 12 * 60, class: 'business-hours' },
    { from: 14 * 60, to: 18 * 60, class: 'business-hours' }
  ],
  4: dailyHours,
  5: dailyHours
}
// `from` and `to` are expected in minutes. const dailyHours = { from: 9 * 60, to: 18 * 60, class: 'business-hours' } // In your component's data, special hours from Monday to Friday. // Note that you can provide an array of multiple blocks for the same day. specialHours: { 1: dailyHours, 2: dailyHours, 3: [ { from: 9 * 60, to: 12 * 60, class: 'business-hours' }, { from: 14 * 60, to: 18 * 60, class: 'business-hours' } ], 4: dailyHours, 5: dailyHours }
.business-hours {
  background-color: rgba(255, 255, 0, 0.15);
  border: solid rgba(255, 210, 0, 0.3);
  border-width: 2px 0;
}
.business-hours { background-color: rgba(255, 255, 0, 0.15); border: solid rgba(255, 210, 0, 0.3); border-width: 2px 0; }

With the same principle, you could also build a lot more complex layout such as the following one.

13 ( 2025)
 24
 25
 26
 27
 28
 29
 30
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
Doctor 1
Full day shift
Doctor 2
Full day shift
Doctor 1
Morning shift
Doctor 3
Afternoon shift
Doctor 1
Full day shift
Doctor 3
Full day shift
Doctor 2
Full day shift
Closed
<vue-cal
  :disable-views="['years', 'year', 'month']"
  :time-from="7 * 60"
  :time-to="20 * 60"
  :special-hours="specialHours" />
<vue-cal :disable-views="['years', 'year', 'month']" :time-from="7 * 60" :time-to="20 * 60" :special-hours="specialHours" />
// In your component's data, special hours from Monday to Sunday (1 to 7).
// Note that you can provide an array of multiple blocks for the same day.
specialHours: {
  1: {
    from: 8 * 60,
    to: 17 * 60,
    class: 'doctor-1',
    label: 'Doctor 1Full day shift'
  },
  2: {
    from: 9 * 60,
    to: 18 * 60,
    class: 'doctor-2',
    label: 'Doctor 2Full day shift'
  },
  3: [
    {
      from: 8 * 60,
      to: 12 * 60,
      class: 'doctor-1',
      label: 'Doctor 1Morning shift'
    },
    {
      from: 14 * 60,
      to: 19 * 60,
      class: 'doctor-3',
      label: 'Doctor 3Afternoon shift'
    }
  ],
  4: {
    from: 8 * 60,
    to: 17 * 60,
    class: 'doctor-1',
    label: 'Doctor 1Full day shift'
  },
  5: {
    from: 9 * 60,
    to: 18 * 60,
    class: 'doctor-3',
    label: 'Doctor 3Full day shift'
  },
  6: {
    from: 9 * 60,
    to: 18 * 60,
    class: 'doctor-2',
    label: 'Doctor 2Full day shift'
  },
  7: {
    from: 7 * 60,
    to: 20 * 60,
    class: 'closed',
    label: 'Closed'
  }
}
// In your component's data, special hours from Monday to Sunday (1 to 7). // Note that you can provide an array of multiple blocks for the same day. specialHours: { 1: { from: 8 * 60, to: 17 * 60, class: 'doctor-1', label: 'Doctor 1
Full day shift' }, 2: { from: 9 * 60, to: 18 * 60, class: 'doctor-2', label: 'Doctor 2
Full day shift' }, 3: [ { from: 8 * 60, to: 12 * 60, class: 'doctor-1', label: 'Doctor 1
Morning shift' }, { from: 14 * 60, to: 19 * 60, class: 'doctor-3', label: 'Doctor 3
Afternoon shift' } ], 4: { from: 8 * 60, to: 17 * 60, class: 'doctor-1', label: 'Doctor 1
Full day shift' }, 5: { from: 9 * 60, to: 18 * 60, class: 'doctor-3', label: 'Doctor 3
Full day shift' }, 6: { from: 9 * 60, to: 18 * 60, class: 'doctor-2', label: 'Doctor 2
Full day shift' }, 7: { from: 7 * 60, to: 20 * 60, class: 'closed', label: 'Closed' } }
.vuecal__special-hours {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 4px;

  em {font-size: 0.9em;color: #999;}
}

.doctor-1 {background-color: #f0fff1;color: #81d58b;}
.doctor-2 {background-color: #f0f6ff;color: #689bee;}
.doctor-3 {background-color: #fcf0ff;color: #d168ee;}
.closed {
  background:
    #fff7f0
    repeating-linear-gradient(
      -45deg,
      rgba(255, 162, 87, 0.25),
      rgba(255, 162, 87, 0.25) 5px,
      rgba(255, 255, 255, 0) 5px,
      rgba(255, 255, 255, 0) 15px
    );
  color: #f6984c;
}
.vuecal__special-hours { display: flex; justify-content: center; align-items: center; padding: 4px; em {font-size: 0.9em;color: #999;} } .doctor-1 {background-color: #f0fff1;color: #81d58b;} .doctor-2 {background-color: #f0f6ff;color: #689bee;} .doctor-3 {background-color: #fcf0ff;color: #d168ee;} .closed { background: #fff7f0 repeating-linear-gradient( -45deg, rgba(255, 162, 87, 0.25), rgba(255, 162, 87, 0.25) 5px, rgba(255, 255, 255, 0) 5px, rgba(255, 255, 255, 0) 15px ); color: #f6984c; }

# Today's current time

When you choose to show the time in vue-cal, the current time of today's date will be marked with a line (scroll to the current time to see it).
The line position will be updated every time the calendar current view is re-rendered (by interacting).
You can easily customize the now-line as you wish via CSS. Changing the line and arrow color is as easy as:

.vuecal__now-line {color: #06c;}
.vuecal__now-line {color: #06c;}

If you don't want this feature you can simply hide it: .vuecal__now-line {display: none}.
This feature has no impact on performance.

If you want the now line to keep accurate position even while your calendar is iddle, you can use the option watchRealTime (see more in the API section).

00:00
01:00
02:00
03:00
04:00
05:00
06:00
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
23:00
<vue-cal xsmall active-view="day" :disable-views="['years', 'year', 'month']" />
<vue-cal xsmall active-view="day" :disable-views="['years', 'year', 'month']" />

# Adding a Today button

By default the selected date is today. But if you get lost in time travel, you can add a Today button to select Today's date with the option today-button.
Like navigation arrows, there is also a slot to customize as you want.
below are the default Today button on the left and a custom one with icon and tooltip on the right.

If you are not satisfied with the position of this button, you can also place it outside of Vue Cal like so:
You might want to change view as well when going to Today's date, here is an example how:Today Button

1
2
3
4
5
8
9
10
11
12
15
16
17
18
19
22
23
24
25
26
29
30
31
1
2
5
6
7
8
9
1
2
3
4
5
8
9
10
11
12
15
16
17
18
19
22
23
24
25
26
29
30
31
1
2
5
6
7
8
9
<vue-cal
  ref="vuecal"
  xsmall
  hide-weekends
  :disable-views="['years']"
  :time="false"
  today-button
  active-view="month"
  :selected-date="selectedDate">
  <!-- Optional slot for the custom button. -->
  <template #today-button>
    <!-- Using Vuetify (but we prefer Wave UI 🤘) -->
    <v-tooltip>
      <template #activator="{ on }">
        <v-btn v-on="on">
          <v-icon>my_location</v-icon>
        </v-btn>
        <span>Go to Today's date</span>
      </template>
    </v-tooltip>
  </template>
</vue-cal>

<button @click="selectedDate = new Date()">ANOTHER TODAY BUTTON</button>
<vue-cal ref="vuecal" xsmall hide-weekends :disable-views="['years']" :time="false" today-button active-view="month" :selected-date="selectedDate"> <!-- Optional slot for the custom button. --> <template #today-button> <!-- Using Vuetify (but we prefer Wave UI 🤘) --> <v-tooltip> <template #activator="{ on }"> <v-btn v-on="on"> <v-icon>my_location</v-icon> </v-btn> <span>Go to Today's date</span> </template> </v-tooltip> </template> </vue-cal> <button @click="selectedDate = new Date()">ANOTHER TODAY BUTTON</button>
data: () => ({
  // Default to next new year eve.
  selectedDate: new Date(new Date().getFullYear(), 11, 31)
})
data: () => ({ // Default to next new year eve. selectedDate: new Date(new Date().getFullYear(), 11, 31) })

Events

# Timeless events

The events have associated dates but no time information.
Timeless events cannot be resized as they have no time or duration information.
Refer to the events option in the API section.

47 ( 2018)
 19
 20
 21
 22
 23
Need to go shopping
shopping_cart
Golf with John
golf_course
Dad's birthday!
cake
Black Friday
shopping_cart
<vue-cal selected-date="2018-11-19"
         :time="false"
         :disable-views="['years', 'year', 'month']"
         hide-weekends
         :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time="false" :disable-views="['years', 'year', 'month']" hide-weekends :events="events"> </vue-cal>
data: () => ({
  events: [
    {
      start: '2018-11-21',
      end: '2018-11-21',
      title: 'Need to go shopping',
      content: '<i class="icon material-icons">shopping_cart</i>',
      class: 'leisure'
    },
    {
      start: '2018-11-21',
      end: '2018-11-21',
      title: 'Golf with John',
      content: '<i class="icon material-icons">golf_course</i>',
      class: 'sport'
    },
    {
      start: '2018-11-22',
      end: '2018-11-22',
      title: 'Dad\'s birthday!',
      content: '<i class="icon material-icons">cake</i>',
      class: 'sport'
    }
  ]
data: () => ({ events: [ { start: '2018-11-21', end: '2018-11-21', title: 'Need to go shopping', content: '<i class="icon material-icons">shopping_cart</i>', class: 'leisure' }, { start: '2018-11-21', end: '2018-11-21', title: 'Golf with John', content: '<i class="icon material-icons">golf_course</i>', class: 'sport' }, { start: '2018-11-22', end: '2018-11-22', title: 'Dad\'s birthday!', content: '<i class="icon material-icons">cake</i>', class: 'sport' } ]
/* Different color for different event types. */
.vuecal__event.leisure {background-color: rgba(253, 156, 66, 0.9);border: 1px solid rgb(233, 136, 46);color: #fff;}
.vuecal__event.sport {background-color: rgba(255, 102, 102, 0.9);border: 1px solid rgb(235, 82, 82);color: #fff;}
/* Different color for different event types. */ .vuecal__event.leisure {background-color: rgba(253, 156, 66, 0.9);border: 1px solid rgb(233, 136, 46);color: #fff;} .vuecal__event.sport {background-color: rgba(255, 102, 102, 0.9);border: 1px solid rgb(235, 82, 82);color: #fff;}

# Events with time information

Read-only events (by default events are not editable) with custom HTML content and css class (for event types).
Note that the events are always selectable (drop shadow and higher z-index), even when uneditable. The difference with timeless events is that a time is set in the start and end attributes of the events.

47 ( 2018)
 19
 20
 21
 22
 23
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Doctor appointment
10:35 - 11:30
local_hospital
Dentist appointment
18:30 - 19:15
local_hospital
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
Swimming lesson
19:30 - 23:00
pool
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
<vue-cal
  selected-date="2018-11-19"
  :time-from="9 * 60"
  :time-to="23 * 60"
  :disable-views="['years', 'year', 'month']"
  hide-weekends
  :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="9 * 60" :time-to="23 * 60" :disable-views="['years', 'year', 'month']" hide-weekends :events="events"> </vue-cal>
events: [
  {
    start: '2018-11-16 10:30',
    end: '2018-11-16 11:30',
    // You can also define event dates with Javascript Date objects:
    // start: new Date(2018, 11 - 1, 16, 10, 30),
    // end: new Date(2018, 11 - 1, 16, 11, 30),
    title: 'Doctor appointment',
    content: '<i class="icon material-icons">local_hospital</i>',
    class: 'health'
  },
  ...
]
events: [ { start: '2018-11-16 10:30', end: '2018-11-16 11:30', // You can also define event dates with Javascript Date objects: // start: new Date(2018, 11 - 1, 16, 10, 30), // end: new Date(2018, 11 - 1, 16, 11, 30), title: 'Doctor appointment', content: '<i class="icon material-icons">local_hospital</i>', class: 'health' }, ... ]

# Open a dialog box on event click / dblclick

By passing a function to the option on-event-click or on-event-dblclick, you can control what happens when you click or double click an event - on any view where the events are displayed.
The callback function you provide will receive 2 arguments:

  • event: the clicked calendar event's object
  • e: the associated javascript DOM event

You can set any custom attribute you want on an event, you will then be able to access it in the dialog box!

 19
 20
 21
 22
 23
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
Need to go shopping
14:00 - 18:00
Click to see my shopping list
Golf with John
10:00 - 15:00
Do I need to tell how many holes?
<vue-cal
  selected-date="2018-11-19"
  :time-from="9 * 60"
  :time-to="19 * 60"
  :disable-views="['years', 'year']"
  hide-weekends
  :events="events"
  :on-event-click="onEventClick">
</vue-cal>

<!-- Using Vuetify (but we prefer Wave UI 🤘) -->
<v-dialog v-model="showDialog">
  <v-card>
    <v-card-title>
      <v-icon>{{ selectedEvent.icon }}</v-icon>
      <span>{{ selectedEvent.title }}</span>
      <v-spacer/>
      <strong>{{ selectedEvent.start && selectedEvent.start.format('DD/MM/YYYY') }}</strong>
    </v-card-title>
    <v-card-text>
      <p v-html="selectedEvent.contentFull"/>
      <strong>Event details:</strong>
      <ul>
        <li>Event starts at: {{ selectedEvent.start && selectedEvent.start.formatTime() }}</li>
        <li>Event ends at: {{ selectedEvent.end && selectedEvent.end.formatTime() }}</li>
      </ul>
    </v-card-text>
  </v-card>
</v-dialog>
<vue-cal selected-date="2018-11-19" :time-from="9 * 60" :time-to="19 * 60" :disable-views="['years', 'year']" hide-weekends :events="events" :on-event-click="onEventClick"> </vue-cal> <!-- Using Vuetify (but we prefer Wave UI 🤘) --> <v-dialog v-model="showDialog"> <v-card> <v-card-title> <v-icon>{{ selectedEvent.icon }}</v-icon> <span>{{ selectedEvent.title }}</span> <v-spacer/> <strong>{{ selectedEvent.start && selectedEvent.start.format('DD/MM/YYYY') }}</strong> </v-card-title> <v-card-text> <p v-html="selectedEvent.contentFull"/> <strong>Event details:</strong> <ul> <li>Event starts at: {{ selectedEvent.start && selectedEvent.start.formatTime() }}</li> <li>Event ends at: {{ selectedEvent.end && selectedEvent.end.formatTime() }}</li> </ul> </v-card-text> </v-card> </v-dialog>
data: () => ({
  selectedEvent: {},
  showDialog: false,
  events: [
    {
      start: '2018-11-20 14:00',
      end: '2018-11-20 18:00',
      title: 'Need to go shopping',
      icon: 'shopping_cart', // Custom attribute.
      content: 'Click to see my shopping list',
      contentFull: 'My shopping list is rather long:<br><ul><li>Avocados</li><li>Tomatoes</li><li>Potatoes</li><li>Mangoes</li></ul>', // Custom attribute.
      class: 'leisure'
    },
    {
      start: '2018-11-22 10:00',
      end: '2018-11-22 15:00',
      title: 'Golf with John',
      icon: 'golf_course', // Custom attribute.
      content: 'Do I need to tell how many holes?',
      contentFull: 'Okay.<br>It will be a 18 hole golf course.', // Custom attribute.
      class: 'sport'
    }
  ]
}),
methods: {
  onEventClick (event, e) {
    this.selectedEvent = event
    this.showDialog = true

    // Prevent navigating to narrower view (default vue-cal behavior).
    e.stopPropagation()
  }
}
data: () => ({ selectedEvent: {}, showDialog: false, events: [ { start: '2018-11-20 14:00', end: '2018-11-20 18:00', title: 'Need to go shopping', icon: 'shopping_cart', // Custom attribute. content: 'Click to see my shopping list', contentFull: 'My shopping list is rather long:<br><ul><li>Avocados</li><li>Tomatoes</li><li>Potatoes</li><li>Mangoes</li></ul>', // Custom attribute. class: 'leisure' }, { start: '2018-11-22 10:00', end: '2018-11-22 15:00', title: 'Golf with John', icon: 'golf_course', // Custom attribute. content: 'Do I need to tell how many holes?', contentFull: 'Okay.<br>It will be a 18 hole golf course.', // Custom attribute. class: 'sport' } ] }), methods: { onEventClick (event, e) { this.selectedEvent = event this.showDialog = true // Prevent navigating to narrower view (default vue-cal behavior). e.stopPropagation() } }
.vuecal__event {cursor: pointer;}

.vuecal__event-title {
  font-size: 1.2em;
  font-weight: bold;
  margin: 4px 0 8px;
}

.vuecal__event-time {
  display: inline-block;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.2);
}

.vuecal__event-content {
  font-style: italic;
}
.vuecal__event {cursor: pointer;} .vuecal__event-title { font-size: 1.2em; font-weight: bold; margin: 4px 0 8px; } .vuecal__event-time { display: inline-block; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid rgba(0, 0, 0, 0.2); } .vuecal__event-content { font-style: italic; }

# Events indicators - years, year & month views

When you define events the month view will display an events count per day.
You can use the option eventsCountOnYearView to show the events count on years & year views as well.
You can customize the events count as you wish via CSS.

Choose an indicator style:

29
30
1
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
17
18
19
2
20
1
21
2
22
23
2
24
25
26
27
28
29
30
1
1
2
3
4
5
6
7
8
9
29
30
1
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
17
18
19
2
20
1
21
2
22
23
2
24
25
26
27
28
29
30
1
1
2
3
4
5
6
7
8
9
<vue-cal
  selected-date="2018-11-19"
  xsmall
  :time-from="10 * 60"
  :disable-views="['day']"
  events-count-on-year-view
  active-view="month"
  :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" xsmall :time-from="10 * 60" :disable-views="['day']" events-count-on-year-view active-view="month" :events="events"> </vue-cal>
/* Default indicator is count, but you can override it with one of the following rules. */

/* Dash indicator */
.vuecal__cell-events-count {
  width: 18px;
  height: 2px;
  color: transparent;
}

/* Dot indicator */
.vuecal__cell-events-count {
  width: 4px;
  min-width: 0;
  height: 4px;
  padding: 0;
  color: transparent;
}

/* Cell background indicator */
.vuecal__cell--has-events {background-color: #fffacd;}
.vuecal__cell-events-count {display: none;}
/* Default indicator is count, but you can override it with one of the following rules. */ /* Dash indicator */ .vuecal__cell-events-count { width: 18px; height: 2px; color: transparent; } /* Dot indicator */ .vuecal__cell-events-count { width: 4px; min-width: 0; height: 4px; padding: 0; color: transparent; } /* Cell background indicator */ .vuecal__cell--has-events {background-color: #fffacd;} .vuecal__cell-events-count {display: none;}

# Display events on month view

With the option events-on-month-view, you can choose whether to display the events on the month view or not.
events-on-month-view accepts a Boolean to show or hide, or the string 'short' to show only the event's title.
If events-on-month-view is set to true, all the informations are displayed, you can then hide any event information via CSS.
If you want all the cells to have the same height on this view, this is also your call, you can do it via CSS.

2018
29
30
Doctor appointment
31
1
2
5
6
7
8
9
12
13
14
15
16
Doctor appointment
19
Doctor appointment
Dentist appointment
20
Crossfit
21
Brunch with Jane
Swimming lesson
22
23
Macca's with Mark
Movie time
26
27
28
29
30
Another movie tonight
3
4
5
6
7
<vue-cal
  selected-date="2018-11-19"
  :time-from="9 * 60"
  :disable-views="['years', 'year']"
  active-view="month"
  hide-weekends
  events-on-month-view="short"
  :events="events"
  style="height: 600px">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="9 * 60" :disable-views="['years', 'year']" active-view="month" hide-weekends events-on-month-view="short" :events="events" style="height: 600px"> </vue-cal>
.vuecal--month-view .vuecal__cell {height: 80px;}

.vuecal--month-view .vuecal__cell-content {
  justify-content: flex-start;
  height: 100%;
  align-items: flex-end;
}

.vuecal--month-view .vuecal__cell-date {padding: 4px;}
.vuecal--month-view .vuecal__no-event {display: none;}
.vuecal--month-view .vuecal__cell {height: 80px;} .vuecal--month-view .vuecal__cell-content { justify-content: flex-start; height: 100%; align-items: flex-end; } .vuecal--month-view .vuecal__cell-date {padding: 4px;} .vuecal--month-view .vuecal__no-event {display: none;}

# Edit & delete events

The editable-events option allows or prevent all these actions when it is set to true or false:

  • Edit the event title
  • Resize an event by dragging the resizer handle. Not available if no timeline, not possible on background events.
  • Drag & drop an event (not from the editable title text selection and not from the resizer). Not possible on background events.
  • Delete an event (by clicking and holding an event)
  • Create a new event (by clicking and dragging on a cell or clicking and holding on a cell)
    Learn more about event creation in the create events example.
But the editable-events option also accept an object to specifically allow or deny any of the previously listed actions.
For instance this object only denies the drag action:
{ title: true, drag: false, resize: true, delete: true, create: true }
{ title: true, drag: false, resize: true, delete: true, create: true }

  • On top of the global actions allowance, you can deny each of these actions individually for each event with the event attributes titleEditable: false, deletable: false, draggable: false & resizable: false.
  • By default the delete button only appears at the top of the event with a set height (1.4em). If you want a full-height delete button like in this example, you can apply the CSS class .vuecal--full-height-delete to your <vue-cal> tag.

In this example, the event creation and drag ability are disabled to focus on edition and deletion.

 19
 20
 21
 22
 23
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Doctor appointment
10:35 - 11:30
local_hospital
Dentist appointment
18:30 - 19:15
local_hospital
Boring event
14:00 - 17:30
block
I am not draggable, not resizable and not deletable.
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
Swimming lesson
19:30 - 23:00
pool
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
<vue-cal selected-date="2018-11-19"
         :time-from="10 * 60"
         :time-to="23 * 60"
         :disable-views="['years', 'year']"
         hide-view-selector
         hide-weekends
         :editable-events="{ title: true, drag: false, resize: true, delete: true, create: false }"
         :events="events"
         class="vuecal--full-height-delete">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="10 * 60" :time-to="23 * 60" :disable-views="['years', 'year']" hide-view-selector hide-weekends :editable-events="{ title: true, drag: false, resize: true, delete: true, create: false }" :events="events" class="vuecal--full-height-delete"> </vue-cal>
// In data.
events: [
  {
    start: '2018-11-20 14:00',
    end: '2018-11-20 17:30',
    title: 'Boring event',
    content: '<i class="icon material-icons">block</i><br>I am not draggable, not resizable and not deletable.',
    class: 'blue-event',
    deletable: false,
    resizable: false,
    draggable: false
  },
  // other events.
]
// In data. events: [ { start: '2018-11-20 14:00', end: '2018-11-20 17:30', title: 'Boring event', content: '<i class="icon material-icons">block</i><br>I am not draggable, not resizable and not deletable.', class: 'blue-event', deletable: false, resizable: false, draggable: false }, // other events. ]
.vuecal__event {background-color: rgba(76, 172, 175, 0.35);}
.vuecal__event {background-color: rgba(76, 172, 175, 0.35);}

# Create events

The event creation is only possible on a day cell, so not on years & year views.
There are multiple ways to create an event, let's start with the default one.

You may also want to observe the emitted events in the emitted events example.

With the snapToTime option, you can make sure the event starts and end at specific intervals of minutes.
E.g. :snap-to-time="15" will snap the event to the closest :00, :15, :30, :45 while dragging.
This option also applies on event resizing after the drag-creation.

Click and drag on a cell to create an event, downwards or upwards.
 24
 25
 26
 27
 28
10:00
11:00
12:00
13:00
14:00
15:00
<vue-cal
  hide-view-selector
  hide-title-bar
  hide-weekends
  :time-from="10 * 60"
  :time-to="16 * 60"
  :disable-views="['years', 'year', 'month', 'day']"
  :editable-events="{ title: false, drag: false, resize: true, delete: true, create: true }"
  :drag-to-create-threshold="0">
</vue-cal>
<vue-cal hide-view-selector hide-title-bar hide-weekends :time-from="10 * 60" :time-to="16 * 60" :disable-views="['years', 'year', 'month', 'day']" :editable-events="{ title: false, drag: false, resize: true, delete: true, create: true }" :drag-to-create-threshold="0"> </vue-cal>

This event creation method can cause difficulty when the calendar allows a click on a cell to navigate: a slightly slipping click would create an event instead of navigating.
For this reason, the dragToCreateThreshold option default is 15 pixels. So if you try to click or double click, it will not create an event.

In this example, the event "drag-creation" only starts after dragging 15 pixels, which allows navigating even with an accidental move while double-clicking.

try to double click on a cell to go to the day view with both dragToCreateThreshold to 15 and 0.

dragToCreateThreshold (px):
13 ( 2025)
 24
 25
 26
 27
 28
10:00
11:00
12:00
13:00
14:00
15:00

# Other event creation methods

There are 3 other ways to create an event: on cell click & hold, on cell single/double click, or programmatically.

Event creation will not trigger with a single/double click or click & hold if your cursor is on an event.

Let's see the 3 cases in order of complexity:

  1. On cell single or double click

    As the cell-click & cell-dblclick emitted events return a date and time at cursor position (refer to the emitted events example), you simply need to call the createEvent() function straight away from cell-dblclick:

     24
     25
     26
     27
     28
    10:00
    11:00
    12:00
    13:00
    14:00
    15:00
    <vue-cal
      ref="vuecal"
      small
      hide-view-selector
      hide-weekends
      hide-title-bar
      :time-from="10 * 60"
      :time-to="16 * 60"
      :disable-views="['years', 'year']"
      :cell-click-hold="false"
      :drag-to-create-event="false"
      editable-events
      @cell-dblclick="$refs.vuecal.createEvent(
        $event,
        120,
        { title: 'New Event', class: 'blue-event' }
      )">
    </vue-cal>
    <vue-cal ref="vuecal" small hide-view-selector hide-weekends hide-title-bar :time-from="10 * 60" :time-to="16 * 60" :disable-views="['years', 'year']" :cell-click-hold="false" :drag-to-create-event="false" editable-events @cell-dblclick="$refs.vuecal.createEvent( $event, 120, { title: 'New Event', class: 'blue-event' } )"> </vue-cal>

    You may then want to disable the default event creation on cell click & hold by setting :cell-click-hold="false"

  2. Programmatically & externally

    To allow an external button to create events, you will need to call the vue-cal createEvent() function from a Vue ref.

    Thiswill prompt you to choose a date and time as the event start.
     24
     25
     26
     27
     28
    10:00
    11:00
    12:00
    13:00
    14:00
    15:00
    <button @click="customEventCreation">
      button
    </button>
    
    <vue-cal
      ref="vuecal"
      small
      :time-from="10 * 60"
      :time-to="16 * 60"
      :disable-views="['years', 'year']"
      hide-view-selector
      hide-title-bar
      hide-weekends
      editable-events
      :cell-click-hold="false"
      :drag-to-create-event="false">
    </vue-cal>
    <button @click="customEventCreation"> button </button> <vue-cal ref="vuecal" small :time-from="10 * 60" :time-to="16 * 60" :disable-views="['years', 'year']" hide-view-selector hide-title-bar hide-weekends editable-events :cell-click-hold="false" :drag-to-create-event="false"> </vue-cal>

    Then you can give custom event attributes as you wish:

    // In methods.
    customEventCreation () {
        const dateTime = prompt('Create event on (YYYY-MM-DD HH:mm)', '2025-03-28 13:15')
    
        // Check if date format is correct before creating event.
        if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(dateTime)) {
          this.$refs.vuecal.createEvent(
            // Formatted start date and time or JavaScript Date object.
            dateTime,
            // Event duration in minutes (Integer).
            120,
            // Custom event props (optional).
            { title: 'New Event', content: 'yay! 🎉', class: 'blue-event' }
          )
        } else if (dateTime) alert('Wrong date format.')
    }
    
    // In methods. customEventCreation () { const dateTime = prompt('Create event on (YYYY-MM-DD HH:mm)', '2025-03-28 13:15') // Check if date format is correct before creating event. if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(dateTime)) { this.$refs.vuecal.createEvent( // Formatted start date and time or JavaScript Date object. dateTime, // Event duration in minutes (Integer). 120, // Custom event props (optional). { title: 'New Event', content: 'yay! 🎉', class: 'blue-event' } ) } else if (dateTime) alert('Wrong date format.') }
  3. Adding a dialog box to the cell click & hold behavior

    By default, event will be created with these attributes:

    {
        start: {Date}, // Starting from the cursor position in the clicked day cell.
        end: {Date}, // Event start + 2 hours.
        title: '',
        content: '',
        split /* if any */: {Integer | String} // The current day split id that was clicked.
    }
    
    { start: {Date}, // Starting from the cursor position in the clicked day cell. end: {Date}, // Event start + 2 hours. title: '', content: '', split /* if any */: {Integer | String} // The current day split id that was clicked. }

    If you want to customize those attributes you can modify the event directly through the callback function that you provide to :on-event-create as follows:

    // :on-event-create="onEventCreate", in template.
    
    /**
    * @param event {Object} The newly created event that you can override.
    * @param deleteEventFunction {Function} Allows you to delete this event programmatically.
    * @return {Object | false} The event to be passed back to Vue Cal, or false to reject creation.
    */
    onEventCreate (event, deleteEventFunction) {
        // You can modify event here and return it.
        // You can also return false to reject the event creation.
        return event
    }
    
    // :on-event-create="onEventCreate", in template. /** * @param event {Object} The newly created event that you can override. * @param deleteEventFunction {Function} Allows you to delete this event programmatically. * @return {Object | false} The event to be passed back to Vue Cal, or false to reject creation. */ onEventCreate (event, deleteEventFunction) { // You can modify event here and return it. // You can also return false to reject the event creation. return event }

    In this example, we are adding a dialog box to the cell click & hold.
    The dialog box will allow you to set all the event attributes.

     24
     25
     26
     27
     28
    10:00
    11:00
    12:00
    13:00
    14:00
    15:00
    <vue-cal
        small
        :time-from="10 * 60"
        :time-to="16 * 60"
        :disable-views="['years', 'year']"
        hide-view-selector
        hide-title-bar
        hide-weekends
        editable-events
        :drag-to-create-event="false"
        :on-event-create="onEventCreate">
    </vue-cal>
    <vue-cal small :time-from="10 * 60" :time-to="16 * 60" :disable-views="['years', 'year']" hide-view-selector hide-title-bar hide-weekends editable-events :drag-to-create-event="false" :on-event-create="onEventCreate"> </vue-cal>
  4. <!-- Using Vuetify (but we prefer Wave UI 🤘) -->
    <v-dialog v-model="showEventCreationDialog" :persistent="true" max-width="420">
      <v-card>
        <v-card-title>
          <v-input v-model="selectedEvent.title" placeholder="Event Title" />
        </v-card-title>
        <v-card-text>
          <v-textarea v-model="selectedEvent.content" placeholder="Event Content" />
          <v-flex>
            <v-select
              :items="eventsCssClasses"
              placeholder="Event CSS Class"
              @change="selectedEvent.class = $event"
              :value="selectedEvent.class" />
            <v-switch v-model="selectedEvent.background" label="background Event" />
          </v-flex>
          <v-flex>
            <v-btn @click="cancelEventCreation()">Cancel</v-btn>
            <v-btn @click="closeCreationDialog()">Save</v-btn>
          </v-flex>
        </v-card-text>
      </v-card>
    
    <!-- Using Vuetify (but we prefer Wave UI 🤘) --> <v-dialog v-model="showEventCreationDialog" :persistent="true" max-width="420"> <v-card> <v-card-title> <v-input v-model="selectedEvent.title" placeholder="Event Title" /> </v-card-title> <v-card-text> <v-textarea v-model="selectedEvent.content" placeholder="Event Content" /> <v-flex> <v-select :items="eventsCssClasses" placeholder="Event CSS Class" @change="selectedEvent.class = $event" :value="selectedEvent.class" /> <v-switch v-model="selectedEvent.background" label="background Event" /> </v-flex> <v-flex> <v-btn @click="cancelEventCreation()">Cancel</v-btn> <v-btn @click="closeCreationDialog()">Save</v-btn> </v-flex> </v-card-text> </v-card>
    data: () => ({
      selectedEvent: null,
      showEventCreationDialog: false,
      eventsCssClasses: ['leisure', 'sport', 'health']
    }),
    methods: {
      onEventCreate (event, deleteEventFunction) {
        this.selectedEvent = event
        this.showEventCreationDialog = true
        this.deleteEventFunction = deleteEventFunction
    
        return event
      },
      cancelEventCreation () {
        this.closeCreationDialog()
        this.deleteEventFunction()
      },
      closeCreationDialog () {
        this.showEventCreationDialog = false
        this.selectedEvent = {}
      }
    }
    
    data: () => ({ selectedEvent: null, showEventCreationDialog: false, eventsCssClasses: ['leisure', 'sport', 'health'] }), methods: { onEventCreate (event, deleteEventFunction) { this.selectedEvent = event this.showEventCreationDialog = true this.deleteEventFunction = deleteEventFunction return event }, cancelEventCreation () { this.closeCreationDialog() this.deleteEventFunction() }, closeCreationDialog () { this.showEventCreationDialog = false this.selectedEvent = {} } }

    With the same method, you can open a dialog at the end of the event drag-creation.

     24
     25
     26
     27
     28
    10:00
    11:00
    12:00
    13:00
    14:00
    15:00

    This example uses the same dialog box and cancelEventCreation & closeCreationDialog functions as the previous example.
    Note that event-drag-create gets fired on mouseup of the drag-create, whereas onEventCreate gets called as soon as the event appears on screen, while dragging.

    <vue-cal
      small
      :time-from="10 * 60"
      :time-to="16 * 60"
      :disable-views="['years', 'year']"
      hide-view-selector
      hide-title-bar
      hide-weekends
      editable-events
      :on-event-create="onEventCreate"
      @event-drag-create="showEventCreationDialog = true">
    </vue-cal>
    <vue-cal small :time-from="10 * 60" :time-to="16 * 60" :disable-views="['years', 'year']" hide-view-selector hide-title-bar hide-weekends editable-events :on-event-create="onEventCreate" @event-drag-create="showEventCreationDialog = true"> </vue-cal>
    data: () => ({
      selectedEvent: null,
      showEventCreationDialog: false
    }),
    methods: {
      // Called when drag-create threshold is reached (when the event appears on screen),
      // but before releasing the drag; so, it should not open the dialog box yet.
      onEventCreate (event, deleteEventFunction) {
        this.selectedEvent = event
        this.deleteEventFunction = deleteEventFunction
    
        return event
      }
    }
    
    data: () => ({ selectedEvent: null, showEventCreationDialog: false }), methods: { // Called when drag-create threshold is reached (when the event appears on screen), // but before releasing the drag; so, it should not open the dialog box yet. onEventCreate (event, deleteEventFunction) { this.selectedEvent = event this.deleteEventFunction = deleteEventFunction return event } }

# Event drag & dropLIMITED SUPPORT ON TOUCH DEVICES

In addition to the obvious event dragging itself, there are quite a few things that are good to know about the drag & drop.

  • Drag & drop is a module (to keep Vue Cal light weight).
    For Vue Cal versions that don't support ESM (prior 4.3.4 on Vue 3 or 3.11.0 on Vue 2), it must be loaded separately:
    import 'vue-cal/dist/drag-and-drop.js'.
  • Drag & drop is only available on single day events for now.

Dragging over header
  • While you drag an event over the view selector buttons, or the previous and next arrows, or even the today button, they will get into a highlighted state and if you hold over for a few milliseconds they will change the view so you can drop the event you are holding on another date of the calendar.
  • while dragged over, the previous and next buttons will keep changing the view until you go away from the button.
  • Dragging an event over the today button will take you to Today's date, and if you're in a years or year view it will also go to the next available narrower view from month downwards.
Dragging over a cell
  • If you drag an event over a cell or a split (ref. splitting days), the cell/split gets into a highlighted state, showing you where the event would go if you drop it.
  • You can drop an event in any cell. But because it does not make much sense to drop it into a years or year view, if you hold over a cell in these views or in month view, it will go to the next available narrower view so you can at least see a day cell.
Dropping the event into a cell or somewhere not allowed
  • If you drop the event outside of the calendar or anywhere it's not possible, it will snap back to its original place and the original view will be restored if it was changed by navigating away.
  • If you drop the event in a cell and it would start before midnight (00:00), it is placed at midnight, keeping its duration.
  • If you drop the event in a cell and it would end after midnight (24:00), its duration will be truncated to end at midnight (24:00).
  • By default, when you drop the event it will start exactly where you dropped it, but if you prefer you can use the snapToTime option to dictate where it should snap to (refer to snapToTime in the API section).
    If you wonder why it does not represent the snapping while dragging, it's not possible to do it with the native HTML5 drag & drop.
Emitted events
CSS styles
  • You can change the highlighted style of the header buttons or cells through these CSS classes:
    • .vuecal__view-btn--highlighted
    • .vuecal__today-btn--highlighted
    • .vuecal__arrow--highlighted
    • .vuecal__cell--highlighted
    • .vuecal__cell-split--highlighted
  • You can change the style of the event being dragged through the .vuecal__event--dragging CSS class.
  • While dragging, a copy of the original event is made and that's what you drag (native HTML5 drag & drop behavior). The original event receive the .vuecal__event--static CSS class which hides it with opacity: 0.
    You can use that class to give it a different style.
 19
 20
 21
 22
 23
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Dr 1
Dr 2
Dr 1
Dr 2
Dr 1
Dr 2
Surgery
14:00 - 16:30
restaurant
Dr 1
Dr 2
Dr 1
Dr 2
<vue-cal
  selected-date="2018-11-19"
  today-button
  :time-from="10 * 60"
  :time-to="23 * 60"
  hide-weekends
  :snap-to-time="15"
  editable-events
  :events="events"
  :split-days="[{ id: 1, label: 'Dr 1' }, { id: 2, label: 'Dr 2' }]"
  class="vuecal--full-height-delete">
</vue-cal>
<vue-cal selected-date="2018-11-19" today-button :time-from="10 * 60" :time-to="23 * 60" hide-weekends :snap-to-time="15" editable-events :events="events" :split-days="[{ id: 1, label: 'Dr 1' }, { id: 2, label: 'Dr 2' }]" class="vuecal--full-height-delete"> </vue-cal>
.vuecal__event--dragging {background-color: rgba(60, 60, 60, 0.3);}
.vuecal__event--dragging {background-color: rgba(60, 60, 60, 0.3);}

# External events drag & dropLIMITED SUPPORT ON TOUCH DEVICES

You can drag & drop events from an external source as long as they are HTML5 draggable (this will change when touch devices are supported).
It is also possible to move an event from one calendar to another.

In the external event, you can set a duration property: it will be used to represent the duration of the event on Vue Cal when it has no date.
If the duration is missing, the default will be 2 hours.

Important note when dragging external events into Vue Cal:

With HTML5 drag & drop, when you drop a DOM element to another location, you have to move the element yourself. Now especially because Vue is data driven and a DOM update does not modify the data, you will also have to remove the event from its original data source yourself - unless you want to create a copy.
Learn how in the example source code below.

Ext. Event 1(60 min)
content 1
Ext. Event 2(30 min)
content 2
Ext. Event 3(no duration)
content 3
13 ( 2025)
 24
 25
 26
 27
 28
09:00
10:00
11:00
12:00
13:00
14:00
15:00
13 ( 2025)
 24
 25
 26
 27
 28
09:00
10:00
11:00
12:00
13:00
14:00
15:00
<!-- Three HTML5 draggable events. -->
<div class="external-event"
     v-for="(item, i) in draggables"
     :key="i"
     draggable="true"
     @dragstart="onEventDragStart($event, item)">
     <strong>{{ item.title }}</strong>
     ({{ item.duration ? `${item.duration} min` : 'no duration' }})
  <div>{{ item.content }}</div>
</div>

<vue-cal small
         hide-view-selector
         hide-weekends
         :disable-views="['years', 'year', 'month', 'day']"
         :time-from="9 * 60"
         :time-to="16 * 60"
         editable-events
         @event-drop="onEventDrop">
</vue-cal>
<!-- Three HTML5 draggable events. --> <div class="external-event" v-for="(item, i) in draggables" :key="i" draggable="true" @dragstart="onEventDragStart($event, item)"> <strong>{{ item.title }}</strong> ({{ item.duration ? `${item.duration} min` : 'no duration' }}) <div>{{ item.content }}</div> </div> <vue-cal small hide-view-selector hide-weekends :disable-views="['years', 'year', 'month', 'day']" :time-from="9 * 60" :time-to="16 * 60" editable-events @event-drop="onEventDrop"> </vue-cal>
export default {
  data: () => ({
    draggables: [
      {
        // The id (or however you name it), will help you find which event to delete
        // from the callback triggered on drop into Vue Cal.
        id: 1,
        title: 'Ext. Event 1',
        content: 'content 1',
        duration: 60
      },
      {
        id: 2,
        title: 'Ext. Event 2',
        content: 'content 2',
        duration: 30
      },
      {
        id: 3,
        title: 'Ext. Event 3',
        content: 'content 3'
        // No defined duration here: will default to 2 hours.
      }
    ]
  }),
  methods: {
    onEventDragStart (e, draggable) {
      // Passing the event's data to Vue Cal through the DataTransfer object.
      e.dataTransfer.setData('event', JSON.stringify(draggable))
      e.dataTransfer.setData('cursor-grab-at', e.offsetY)
    },
    // The 3 parameters are destructured from the passed $event in @event-drop="onEventDrop".
    // `event` is the final event as Vue Cal understands it.
    // `originalEvent` is the event that was dragged into Vue Cal, it can come from the same
    //  Vue Cal instance, another one, or an external source.
    // `external` is a boolean that lets you know if the event is not coming from any Vue Cal.
    onEventDrop ({ event, originalEvent, external }) {
      // If the event is external, delete it from the data source on drop into Vue Cal.
      // If the event comes from another Vue Cal instance, it will be deleted automatically in there.
      if (external) {
        const extEventToDeletePos = this.draggables.findIndex(item => item.id === originalEvent.id)
        if (extEventToDeletePos > -1) this.draggables.splice(extEventToDeletePos, 1)
      }
    }
  }
}
export default { data: () => ({ draggables: [ { // The id (or however you name it), will help you find which event to delete // from the callback triggered on drop into Vue Cal. id: 1, title: 'Ext. Event 1', content: 'content 1', duration: 60 }, { id: 2, title: 'Ext. Event 2', content: 'content 2', duration: 30 }, { id: 3, title: 'Ext. Event 3', content: 'content 3' // No defined duration here: will default to 2 hours. } ] }), methods: { onEventDragStart (e, draggable) { // Passing the event's data to Vue Cal through the DataTransfer object. e.dataTransfer.setData('event', JSON.stringify(draggable)) e.dataTransfer.setData('cursor-grab-at', e.offsetY) }, // The 3 parameters are destructured from the passed $event in @event-drop="onEventDrop". // `event` is the final event as Vue Cal understands it. // `originalEvent` is the event that was dragged into Vue Cal, it can come from the same // Vue Cal instance, another one, or an external source. // `external` is a boolean that lets you know if the event is not coming from any Vue Cal. onEventDrop ({ event, originalEvent, external }) { // If the event is external, delete it from the data source on drop into Vue Cal. // If the event comes from another Vue Cal instance, it will be deleted automatically in there. if (external) { const extEventToDeletePos = this.draggables.findIndex(item => item.id === originalEvent.id) if (extEventToDeletePos > -1) this.draggables.splice(extEventToDeletePos, 1) } } } }

# Multiple day events

Multiple day events work like a set of single day events linked together.
Deleting one of the day of a multiple day event, will also delete all the other days.
Updating the duration by dragging or changing the title will also update on all the days.
Try to resize, rename and delete the events.
You can also resize horizontally thanks to the option resize-x.

Drag & drop is not available on multiple day events for now.

3 CSS classes are available to target the event first day, the last day and all the days in between: event-start, event-middle, event-end.

 19
 20
 21
 22
 23
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Running Marathon
10:00 - 12:37
directions_run
Running Marathon
10:00 - 12:37
directions_run
Drink water!
10:00 - 10:25
local_drink
Trip to India
19:00 - 11:30 +2
flight
Trip to India
19:00 - 11:30
flight
Trip to India
19:00 - 11:30
flight
<vue-cal
  selected-date="2018-11-19"
  :time-from="8 * 60"
  :time-to="23 * 60"
  :disable-views="['years', 'year', 'month']"
  hide-weekends
  editable-events
  resize-x
  :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="8 * 60" :time-to="23 * 60" :disable-views="['years', 'year', 'month']" hide-weekends editable-events resize-x :events="events"> </vue-cal>
data: () => ({
  events: [
    {
      start: '2018-11-16 10:00',
      end: '2018-11-20 12:37',
      title: 'Running Marathon',
      content: '<i class="icon material-icons">directions_run</i>',
      class: 'sport'
    },
    {
      start: '2018-11-20 10:00',
      end: '2018-11-20 10:25',
      title: 'Drink water!',
      content: '<i class="icon material-icons">local_drink</i>',
      class: 'health'
    },
    {
      start: '2018-11-21 19:00',
      end: '2018-11-23 11:30',
      title: 'Trip to India',
      content: '<i class="icon material-icons">flight</i>',
      class: 'leisure'
    }
  ]
})
data: () => ({ events: [ { start: '2018-11-16 10:00', end: '2018-11-20 12:37', title: 'Running Marathon', content: '<i class="icon material-icons">directions_run</i>', class: 'sport' }, { start: '2018-11-20 10:00', end: '2018-11-20 10:25', title: 'Drink water!', content: '<i class="icon material-icons">local_drink</i>', class: 'health' }, { start: '2018-11-21 19:00', end: '2018-11-23 11:30', title: 'Trip to India', content: '<i class="icon material-icons">flight</i>', class: 'leisure' } ] })

# Recurring eventsCOMING SOON

When it will be ready, this is how it will work.
You can repeat an event:
  • Every day - by providing a every: "day" property.
  • Every week - by providing a every: "week" property.
  • Every month - by providing a every: "month" property.
  • Every year - by providing a every: "year" property.
  • Every specific week days - by providing a weekdays array containing the weekdays numbers (1 to 7 for Sunday).
  • Every `x` days - by providing a every: x property, with x being an integer.
  • Forever; Or until an expiry date if you provide an until: {String | Date} property.
  • Whether it's single-day, multiple-day, background, all-day, with time or timeless.
// month view event count => OK.
// @todo: check years/year views event counts.
// @todo: repeated multiple-day events does not appear if the first day is not in view (e.g. hide weekend).
// @todo: on month view with show events, occurrences don't appear on out of scope days.
// @todo: overlapping does not work.
// @todo: if 2 occurences are in the same day (multiple-day events), only one is shown.
// @todo: check all the above points one by one.
// month view event count => OK. // @todo: check years/year views event counts. // @todo: repeated multiple-day events does not appear if the first day is not in view (e.g. hide weekend). // @todo: on month view with show events, occurrences don't appear on out of scope days. // @todo: overlapping does not work. // @todo: if 2 occurences are in the same day (multiple-day events), only one is shown. // @todo: check all the above points one by one.

Recurrring events work like a set of single day events linked together.
That means, deleting, resizing or editing one of the day will apply to all the other days.

Demo coming soon.
<vue-cal
  selected-date="2018-11-19"
  :time-from="8 * 60"
  :time-to="23 * 60"
  hide-weekends
  events-count-on-year-view
  editable-events
  show-all-day-events
  :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="8 * 60" :time-to="23 * 60" hide-weekends events-count-on-year-view editable-events show-all-day-events :events="events"> </vue-cal>
data: () => ({
  events: [
    {
      start: '2018-11-19 22:00',
      end: '2018-11-20 11:00',
      title: 'Nightclub',
      content: '<i class="icon material-icons">local_drink</i>',
      class: 'leisure',
      repeat: {
        weekdays: [1, 3], // You can repeat on multiple days of the week.
        until: '2020-11-30' // Don't need a time here as it will take the same as original event date.
      }
    },
    {
      start: '2018-11-23', // You can put time or not, will be discarded if all-day.
      end: '2018-11-23',
      title: 'Pizza day!',
      content: '<i class="icon material-icons">local_pizza</i>',
      class: 'pink-event',
      allDay: true,
      repeat: {
        weekdays: [5] // If original event day is not in these days, original event will still show up.
        // Without `until` property, it will go on forever.
      }
    },
    {
      start: '2018-11-22 10:00',
      end: '2018-11-22 12:00',
      title: 'Piano lesson',
      content: '<i class="icon material-icons">queue_music</i>',
      class: 'leisure',
      repeat: {
        every: 'week',
        until: new Date('2019/06/01') // You can also use a Javascript Date.
      }
    },
    {
      start: '2018-11-20 18:00',
      end: '2018-11-20 20:00',
      title: 'Tennis tournament',
      content: '<i class="icon material-icons">sports_tennis</i>',
      class: 'sport',
      repeat: {
        every: 14,
        until: '2019-01-20'
      }
    },
    {
      start: '2018-11-01',
      end: '2018-11-01',
      title: 'Crêpes day',
      content: '<i class="icon material-icons">restaurant</i>',
      class: 'yellow-event',
      allDay: true,
      repeat: {
        every: 'month',
        until: '2019-12-26'
      }
    },
    {
      start: '2015-06-15',
      end: '2015-06-15',
      title: 'My Birthday',
      content: '<i class="icon material-icons">cake</i><br>I am 4.',
      class: 'blue-event',
      allDay: true,
      repeat: {
        every: 'year'
      }
    }
  ]
})
data: () => ({ events: [ { start: '2018-11-19 22:00', end: '2018-11-20 11:00', title: 'Nightclub', content: '<i class="icon material-icons">local_drink</i>', class: 'leisure', repeat: { weekdays: [1, 3], // You can repeat on multiple days of the week. until: '2020-11-30' // Don't need a time here as it will take the same as original event date. } }, { start: '2018-11-23', // You can put time or not, will be discarded if all-day. end: '2018-11-23', title: 'Pizza day!', content: '<i class="icon material-icons">local_pizza</i>', class: 'pink-event', allDay: true, repeat: { weekdays: [5] // If original event day is not in these days, original event will still show up. // Without `until` property, it will go on forever. } }, { start: '2018-11-22 10:00', end: '2018-11-22 12:00', title: 'Piano lesson', content: '<i class="icon material-icons">queue_music</i>', class: 'leisure', repeat: { every: 'week', until: new Date('2019/06/01') // You can also use a Javascript Date. } }, { start: '2018-11-20 18:00', end: '2018-11-20 20:00', title: 'Tennis tournament', content: '<i class="icon material-icons">sports_tennis</i>', class: 'sport', repeat: { every: 14, until: '2019-01-20' } }, { start: '2018-11-01', end: '2018-11-01', title: 'Crêpes day', content: '<i class="icon material-icons">restaurant</i>', class: 'yellow-event', allDay: true, repeat: { every: 'month', until: '2019-12-26' } }, { start: '2015-06-15', end: '2015-06-15', title: 'My Birthday', content: '<i class="icon material-icons">cake</i><br>I am 4.', class: 'blue-event', allDay: true, repeat: { every: 'year' } } ] })

# Overlapping events

Overlapping, editable & deletable events.
Try to resize & delete events to see the overlapping redrawn.

Optionally you can set a min width (in percent) to the events:

In some cases you may want to set the events overlaps calculation only per same time step (default time step is 1 hour), like in this use case.
You can achieve this event overlaps grouping with the option overlaps-per-time-step.

47 ( 2018)
 19
 20
 21
 22
 23
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Doctor appointment
10:35 - 11:30
local_hospital
Dentist appointment
18:30 - 19:15
local_hospital
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
A big thing
14:00 - 22:00
sentiment_satisfied_alt
Another thing
16:00 - 19:00
thumb_up
Swimming lesson
19:30 - 23:00
pool
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
Eat pop corns
21:00 - 23:30
local_play
Enjoy the movie
21:00 - 23:30
local_play
<vue-cal
  selected-date="2018-11-19"
  :time-from="10 * 60"
  :time-to="23 * 60"
  :disable-views="['years', 'year', 'month']"
  hide-weekends
  editable-events
  :min-event-width="minEventWidth"
  :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="10 * 60" :time-to="23 * 60" :disable-views="['years', 'year', 'month']" hide-weekends editable-events :min-event-width="minEventWidth" :events="events"> </vue-cal>
data: () => ({
  minEventWidth: 0,
  events: [
    {
      start: '2018-11-21 14:00',
      end: '2018-11-21 22:00',
      title: 'A big thing',
      content: '<i class="icon material-icons">sentiment_satisfied_alt</i>',
      class: 'health'
    },
    {
      start: '2018-11-21 16:00',
      end: '2018-11-21 19:00',
      title: 'Another thing',
      content: '<i class="icon material-icons">thumb_up</i>',
      class: 'blue-event'
    },
    {
      start: '2018-11-20 18:30',
      end: '2018-11-20 20:30',
      title: 'Crossfit',
      content: '<i class="icon material-icons">fitness_center</i>',
      class: 'sport'
    },
    ...
  ]
})
data: () => ({ minEventWidth: 0, events: [ { start: '2018-11-21 14:00', end: '2018-11-21 22:00', title: 'A big thing', content: '<i class="icon material-icons">sentiment_satisfied_alt</i>', class: 'health' }, { start: '2018-11-21 16:00', end: '2018-11-21 19:00', title: 'Another thing', content: '<i class="icon material-icons">thumb_up</i>', class: 'blue-event' }, { start: '2018-11-20 18:30', end: '2018-11-20 20:30', title: 'Crossfit', content: '<i class="icon material-icons">fitness_center</i>', class: 'sport' }, ... ] })

# Background events

Just add the property background: true to your events.
The particularity of the background events is that they can fully be overlapped but not overlapping.
They are not affected by other events: they stay in the background occupying the whole cell/split width.
Note that you can still temporarily raise a background event on top of others (z-index) by hovering it or clicking it. Refer to the events option in the API section.

47 ( 2018)
 19
 20
 21
 22
 23
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Doctor appointment
10:35 - 11:30
local_hospital
LUNCH
12:00 - 14:00
Dentist appointment
18:30 - 19:15
local_hospital
LUNCH
12:00 - 14:00
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
LUNCH
12:00 - 14:00
Swimming lesson
19:30 - 23:00
pool
LUNCH
12:00 - 14:00
LUNCH
12:00 - 14:00
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
<vue-cal
  selected-date="2018-11-19"
  :time-from="7 * 60"
  :time-to="23 * 60"
  :disable-views="['years', 'year', 'month']"
  hide-weekends
  :events="events">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="7 * 60" :time-to="23 * 60" :disable-views="['years', 'year', 'month']" hide-weekends :events="events"> </vue-cal>
data: () => ({
  events: [
    {
      start: '2018-11-19 12:00',
      end: '2018-11-19 14:00',
      title: 'LUNCH',
      class: 'lunch',
      background: true
    },
    {
      start: '2018-11-20 12:00',
      end: '2018-11-20 14:00',
      title: 'LUNCH',
      class: 'lunch',
      background: true
    },
    ...
  ]
})
data: () => ({ events: [ { start: '2018-11-19 12:00', end: '2018-11-19 14:00', title: 'LUNCH', class: 'lunch', background: true }, { start: '2018-11-20 12:00', end: '2018-11-20 14:00', title: 'LUNCH', class: 'lunch', background: true }, ... ] })
.vuecal__event.lunch {
  background: repeating-linear-gradient(45deg, transparent, transparent 10px, #f2f2f2 10px, #f2f2f2 20px);/* IE 10+ */
  color: #999;
  display: flex;
  justify-content: center;
  align-items: center;
}
.vuecal__event.lunch .vuecal__event-time {display: none;align-items: center;}
.vuecal__event.lunch { background: repeating-linear-gradient(45deg, transparent, transparent 10px, #f2f2f2 10px, #f2f2f2 20px);/* IE 10+ */ color: #999; display: flex; justify-content: center; align-items: center; } .vuecal__event.lunch .vuecal__event-time {display: none;align-items: center;}

# All day events

  • When the showAllDayEvents is set to true the events with an allDay attribute set to true will be displayed in a fixed top bar on the week & day views.
    The all day events bar will only show up if the options showAllDayEvents & time are set to true.
    time is important since without time information every event is an all-day event there is no point in separating them then.
  • When showAllDayEvents is set to false, all the all day events (allDay attribute set to true), will show up as a normal background event.
  • On month view, switching showAllDayEvents on and off will not have any impact since both should display the all day events.
  • showAllDayEvents accepts a Boolean or the string 'short', to display only the event title.

Multiple-day events feature will be improved in a future version to display across multiple cells in the all day bar.

 11
 12
 13
 14
 15
Day off!
Valentine's day
Need to go shopping
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
23:00
Doctor appointment
10:35 - 11:30
local_hospital
Dentist appointment
18:30 - 19:15
local_hospital
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
Swimming lesson
19:30 - 23:00
pool
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
<button @click="showAllDayEvents = (showAllDayEvents + 1) % 3">
  :show-all-day-events="{{ ["'short'", 'true', 'false'][showAllDayEvents] }}"
</button>
<button @click="shortEventsOnMonthView = !shortEventsOnMonthView">
  :events-on-month-views="{{ ['true', "'short'"][shortEventsOnMonthView * 1] }}"
</button>

<vue-cal
  selected-date="2019-02-11"
  :time-from="7 * 60"
  :disable-views="['years', 'year']"
  hide-weekends
  :show-all-day-events="['short', true, false][showAllDayEvents]"
  :events-on-month-view="[true, 'short'][shortEventsOnMonthView * 1]"
  :events="events">
</vue-cal>
<button @click="showAllDayEvents = (showAllDayEvents + 1) % 3"> :show-all-day-events="{{ ["'short'", 'true', 'false'][showAllDayEvents] }}" </button> <button @click="shortEventsOnMonthView = !shortEventsOnMonthView"> :events-on-month-views="{{ ['true', "'short'"][shortEventsOnMonthView * 1] }}" </button> <vue-cal selected-date="2019-02-11" :time-from="7 * 60" :disable-views="['years', 'year']" hide-weekends :show-all-day-events="['short', true, false][showAllDayEvents]" :events-on-month-view="[true, 'short'][shortEventsOnMonthView * 1]" :events="events"> </vue-cal>
showAllDayEvents: 0,
shortEventsOnMonthView: false,
events: [
  {
    start: '2019-02-12',
    end: '2019-02-12',
    title: 'Day off!',
    content: '<i class="icon material-icons">beach_access</i>',
    class: 'yellow-event',
    allDay: true
  },
  {
    start: '2019-02-14',
    end: '2019-02-14',
    title: 'Valentine\'s day',
    content: '<i class="icon material-icons">favorite_outline</i>',
    class: 'pink-event',
    allDay: true
  },
  ...
]
showAllDayEvents: 0, shortEventsOnMonthView: false, events: [ { start: '2019-02-12', end: '2019-02-12', title: 'Day off!', content: '<i class="icon material-icons">beach_access</i>', class: 'yellow-event', allDay: true }, { start: '2019-02-14', end: '2019-02-14', title: 'Valentine\'s day', content: '<i class="icon material-icons">favorite_outline</i>', class: 'pink-event', allDay: true }, ... ]
.vuecal__cell-content {align-self: flex-start;}
.vuecal__cell-date {text-align: right;padding: 4px;}

.vuecal--week-view .vuecal__bg .vuecal__event--all-day.pink-event,
.vuecal--day-view .vuecal__bg .vuecal__event--all-day.pink-event {right: 50%;}
.vuecal--week-view .vuecal__bg .vuecal__event--all-day.leisure,
.vuecal--day-view .vuecal__bg .vuecal__event--all-day.leisure {left: 50%;}
.vuecal__cell-content {align-self: flex-start;} .vuecal__cell-date {text-align: right;padding: 4px;} .vuecal--week-view .vuecal__bg .vuecal__event--all-day.pink-event, .vuecal--day-view .vuecal__bg .vuecal__event--all-day.pink-event {right: 50%;} .vuecal--week-view .vuecal__bg .vuecal__event--all-day.leisure, .vuecal--day-view .vuecal__bg .vuecal__event--all-day.leisure {left: 50%;}

# Splitting days & split events

Split each day into multiple containers passing a CSS class & a label per split, and allow split-specific events.

By default the body of the calendar will fit the container.
But with the options min-cell-width or min-split-width, you can increase the calendar body width and it will become scrollable horizontally.
  • min-cell-width.black will only be activated on week view, since there is only 1 cell in day view.
  • If both min-cell-width and min-split-width are set, min-split-width will be used.

You can also use the option sticky-split-labels to place the split labels in the header.

You can toggle the splits thanks to the hide property of each split in splitDays.

Refer to the min-cell-width.black, min-split-width and splitDays option in the API section.

08:00
08:30
09:00
09:30
10:00
10:30
11:00
11:30
12:00
12:30
13:00
13:30
14:00
14:30
15:00
15:30
16:00
16:30
17:00
17:30
18:00
18:30
19:00
19:30
20:00
20:30
21:00
21:30
22:00
22:30
23:00
23:30
 19
 20
 21
 22
 23
 24
 25
Mom
Doctor appointment
10:35 - 11:30
local_hospital
Dad
Dentist appointment
18:30 - 19:15
local_hospital
Kid 1
Kid 2
Kid 3
Mom
Dad
Crossfit
18:30 - 20:30
fitness_center
Kid 1
Kid 2
Kid 3
Mom
Brunch with Jane
11:00 - 13:00
local_cafe
Recall Dave
12:00 - 12:30
local_cafe
Salsa
20:00 - 22:00
directions_walk
Dad
Swimming lesson
19:30 - 23:00
pool
Kid 1
Kid 2
Kid 3
Mom
Dad
Kid 1
Kid 2
Kid 3
Mom
Movie time
21:00 - 23:30
local_play
Dad
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
Kid 1
Kid 2
Kid 3
Mom
Dad
Kid 1
Kid 2
Kid 3
Mom
Dad
Kid 1
Kid 2
Kid 3
<button @click="minCellWidth = minCellWidth ? 0 : 400">
  {{ minCellWidth ? 'min cell width: 400px' : 'Add min cell width' }}
</button>
<button @click="minSplitWidth = minSplitWidth ? 0 : 200">
  {{ minSplitWidth ? 'min split width: 200px' : 'Add min split width' }}
</button>
<button @click="stickySplitLabels = !stickySplitLabels">
  Sticky Split Labels
</button>
<button @click="splitDays[1].hide = !splitDays[1].hide">
  Show/Hide Dad
</button>

<vue-cal
  selected-date="2018-11-19"
  :time-from="8 * 60"
  :time-step="30"
  :disable-views="['years', 'year', 'month']"
  editable-events
  :events="events"
  :split-days="splitDays"
  :sticky-split-labels="stickySplitLabels"
  :min-cell-width="minCellWidth"
  :min-split-width="minSplitWidth">
</vue-cal>
<button @click="minCellWidth = minCellWidth ? 0 : 400"> {{ minCellWidth ? 'min cell width: 400px' : 'Add min cell width' }} </button> <button @click="minSplitWidth = minSplitWidth ? 0 : 200"> {{ minSplitWidth ? 'min split width: 200px' : 'Add min split width' }} </button> <button @click="stickySplitLabels = !stickySplitLabels"> Sticky Split Labels </button> <button @click="splitDays[1].hide = !splitDays[1].hide"> Show/Hide Dad </button> <vue-cal selected-date="2018-11-19" :time-from="8 * 60" :time-step="30" :disable-views="['years', 'year', 'month']" editable-events :events="events" :split-days="splitDays" :sticky-split-labels="stickySplitLabels" :min-cell-width="minCellWidth" :min-split-width="minSplitWidth"> </vue-cal>
data: () => ({
  stickySplitLabels: false,
  minCellWidth: 400,
  minSplitWidth: 0,
  splitDays: [
    // The id property is added automatically if none (starting from 1), but you can set a custom one.
    // If you need to toggle the splits, you must set the id explicitly.
    { id: 1, class: 'mom', label: 'Mom' },
    { id: 2, class: 'dad', label: 'Dad', hide: false },
    { id: 3, class: 'kid1', label: 'Kid 1' },
    { id: 4, class: 'kid2', label: 'Kid 2' },
    { id: 5, class: 'kid3', label: 'Kid 3' }
  ]
  events: [
    {
      start: '2018-11-19 10:35',
      end: '2018-11-19 11:30',
      title: 'Doctor appointment',
      content: '<i class="icon material-icons">local_hospital</i>',
      class: 'health',
      split: 1 // Has to match the id of the split you have set (or integers if none).
    },
    {
      start: '2018-11-19 18:30',
      end: '2018-11-19 19:15',
      title: 'Dentist appointment',
      content: '<i class="icon material-icons">local_hospital</i>',
      class: 'health',
      split: 2
    },
    {
      start: '2018-11-20 18:30',
      end: '2018-11-20 20:30',
      title: 'Crossfit',
      content: '<i class="icon material-icons">fitness_center</i>',
      class: 'sport',
      split: 1
    },
    ...
  ]
})
data: () => ({ stickySplitLabels: false, minCellWidth: 400, minSplitWidth: 0, splitDays: [ // The id property is added automatically if none (starting from 1), but you can set a custom one. // If you need to toggle the splits, you must set the id explicitly. { id: 1, class: 'mom', label: 'Mom' }, { id: 2, class: 'dad', label: 'Dad', hide: false }, { id: 3, class: 'kid1', label: 'Kid 1' }, { id: 4, class: 'kid2', label: 'Kid 2' }, { id: 5, class: 'kid3', label: 'Kid 3' } ] events: [ { start: '2018-11-19 10:35', end: '2018-11-19 11:30', title: 'Doctor appointment', content: '<i class="icon material-icons">local_hospital</i>', class: 'health', split: 1 // Has to match the id of the split you have set (or integers if none). }, { start: '2018-11-19 18:30', end: '2018-11-19 19:15', title: 'Dentist appointment', content: '<i class="icon material-icons">local_hospital</i>', class: 'health', split: 2 }, { start: '2018-11-20 18:30', end: '2018-11-20 20:30', title: 'Crossfit', content: '<i class="icon material-icons">fitness_center</i>', class: 'sport', split: 1 }, ... ] })
/* You can easily set a different style for each split of your days. */
.vuecal__cell-split.dad {background-color: rgba(221, 238, 255, 0.5);}
.vuecal__cell-split.mom {background-color: rgba(255, 232, 251, 0.5);}
.vuecal__cell-split.kid1 {background-color: rgba(221, 255, 239, 0.5);}
.vuecal__cell-split.kid2 {background-color: rgba(255, 250, 196, 0.5);}
.vuecal__cell-split.kid3 {background-color: rgba(255, 206, 178, 0.5);}
.vuecal__cell-split .split-label {color: rgba(0, 0, 0, 0.1);font-size: 26px;}

/* Different color for different event types. */
.vuecal__event.leisure {background-color: rgba(253, 156, 66, 0.9);border: 1px solid rgb(233, 136, 46);color: #fff;}
.vuecal__event.health {background-color: rgba(164, 230, 210, 0.9);border: 1px solid rgb(144, 210, 190);}
.vuecal__event.sport {background-color: rgba(255, 102, 102, 0.9);border: 1px solid rgb(235, 82, 82);color: #fff;}
/* You can easily set a different style for each split of your days. */ .vuecal__cell-split.dad {background-color: rgba(221, 238, 255, 0.5);} .vuecal__cell-split.mom {background-color: rgba(255, 232, 251, 0.5);} .vuecal__cell-split.kid1 {background-color: rgba(221, 255, 239, 0.5);} .vuecal__cell-split.kid2 {background-color: rgba(255, 250, 196, 0.5);} .vuecal__cell-split.kid3 {background-color: rgba(255, 206, 178, 0.5);} .vuecal__cell-split .split-label {color: rgba(0, 0, 0, 0.1);font-size: 26px;} /* Different color for different event types. */ .vuecal__event.leisure {background-color: rgba(253, 156, 66, 0.9);border: 1px solid rgb(233, 136, 46);color: #fff;} .vuecal__event.health {background-color: rgba(164, 230, 210, 0.9);border: 1px solid rgb(144, 210, 190);} .vuecal__event.sport {background-color: rgba(255, 102, 102, 0.9);border: 1px solid rgb(235, 82, 82);color: #fff;}

Communicating with Vue Cal

# Vue Cal emitted events

Vue Cal emits events that you can listen to, to trigger an action outside of Vue Cal.
If you are not familiar with Vue JS events, you should read about it here: vuejs.org/v2/guide/events.html

Here is the list of emitted events:

View-related

  • ready
  • view-change
  • cell-click - returns a JS native Date object
  • cell-dblclick - returns a JS native Date object
  • cell-contextmenu - returns a JS native Date object and x, y: the cursor coordinates.
  • cell-keypress-enter - returns a JS native Date object
  • cell-focus - returns a JS native Date object

  • cell-click is fired every time you click a day, whereas cell-focus is fired only when the selected day changes.
  • cell-click, cell-dblclick, cell-contextmenu and cell-focus return the time at cursor position, unless the cell was focused from tab key. It would then return the cell start date (at midnight).
  • If split-days is provided, cell-click, cell-dblclick, cell-keypress-enter and cell-focus emitted events will return an object containing the date and the clicked split id.

The emitted events ready & view-change return an object:

{
  view: [String],
  startDate: [Date], // View start - JS native Date object.
  endDate: [Date], // View end - JS native Date object.
  firstCellDate: [Date], // Month view only, in case cell is out of current month - JS native Date object.
  lastCellDate: [Date], // Month view only, in case cell is out of current month - JS native Date object.
  outOfScopeEvents: [Array], // Month view only, all the events that are out of the current month.
  events: [Array], // All the events in the current view.
  week: [Integer] // Week number. Only returned if view is 'week'.
}
{ view: [String], startDate: [Date], // View start - JS native Date object. endDate: [Date], // View end - JS native Date object. firstCellDate: [Date], // Month view only, in case cell is out of current month - JS native Date object. lastCellDate: [Date], // Month view only, in case cell is out of current month - JS native Date object. outOfScopeEvents: [Array], // Month view only, all the events that are out of the current month. events: [Array], // All the events in the current view. week: [Integer] // Week number. Only returned if view is 'week'. }
Note that on a month view, the events from the out of scope days (cells before and after the current month) are also returned in the array.

Events-related

  • event-focus - returns the associated calendar event object.
  • event-mouse-enter - returns the associated calendar event object.
  • event-mouse-leave - returns the associated calendar event object.
  • event-create - returns the associated calendar event object.
  • event-drag-create(only fired on mouseup after the event drag creation)

    Returns the associated calendar event object.

  • event-delete - returns the associated calendar event object.
  • event-title-change - returns an object containing:
    • event, the calendar event object that was dropped
    • oldTitle, the title of the event before it was edited
  • event-duration-change(only fired at the end of the event resizing)
    Returns an object containing:
    • event, the calendar event object that was resized
    • oldDate, the Javascript Date the event was ending at before resize
    • originalEvent, the same calendar event before the change
  • event-resizingFired repeatedly while resizing
    For performance while dragging, returns a lighter object containing:
    • _eid, the calendar event internal id.
    • end, the calendar event new end Date.
    • endTimeMinutes, the calendar event new end time in minutes.

    You should only listen to this event if you have no choice. In most of cases you should listen to event-duration-change instead (fired only once at the end of the resizing).

  • event-drop- returns an object containing:
    • event, the calendar event object that was dropped
    • oldDate, the Javascript Date the event was starting from before drag
    • newDate, the Javascript Date the event is now starting from
    • oldSplit only if splitting days, the id of the split the event came from
    • newSplit only if splitting days, the id of the split the event is dropped into
  • event-change - returns an object containing:
    • event, the calendar event object that was changed
    • originalEvent, the same calendar event before the change (null when creating event)

  • The event-change emitted event groups all the events triggered on a calendar event property change: event-title-change, event-drop, event-duration-change and event-create. So you have the choice to listen to event-change to cover any calendar event change or listen to a specific action emitted event.
  • To help you manipulate an event's date, Vue Cal returns native Date objects in the event properties start & end.
    So for instance, you can easily access the day of the week of an event with event.start.getDay().
    You can then use Vue Cal Date prototypes to manipulate and format the Date as you want.

Watch the list of emitted events (latest on top) as you play with Vue Cal:

// event-name: arguments-list
ready:{"view": "week", "startDate": "2018-11-19T00:00:00.000Z", "endDate": "2018-11-23T23:59:59.000Z", "events": [{"_eid": "2629_3", "start": "2018-11-19T10:35:00.000Z", "startTimeMinutes": 635, "end": "2018-11-19T11:30:00.000Z", "endTimeMinutes": 690, "title": "Doctor appointment", "content": "<i class=\"w-icon material-icons\">local_hospital</i>", "background": false, "allDay": false, "daysCount": 1, "class": "health", "split": 1}, {"_eid": "2629_4", "start": "2018-11-19T18:30:00.000Z", "startTimeMinutes": 1110, "end": "2018-11-19T19:15:00.000Z", "endTimeMinutes": 1155, "title": "Dentist appointment", "content": "<i class=\"w-icon material-icons\">local_hospital</i>", "background": false, "allDay": false, "daysCount": 1, "class": "health", "split": 2}, {"_eid": "2629_5", "start": "2018-11-20T18:30:00.000Z", "startTimeMinutes": 1110, "end": "2018-11-20T20:30:00.000Z", "endTimeMinutes": 1230, "title": "Crossfit", "content": "<i class=\"w-icon material-icons\">fitness_center</i>", "background": false, "allDay": false, "daysCount": 1, "class": "sport", "split": 2}, {"_eid": "2629_6", "start": "2018-11-21T11:00:00.000Z", "startTimeMinutes": 660, "end": "2018-11-21T13:00:00.000Z", "endTimeMinutes": 780, "title": "Brunch with Jane", "content": "<i class=\"w-icon material-icons\">local_cafe</i>", "background": false, "allDay": false, "daysCount": 1, "class": "leisure", "split": 1}, {"_eid": "2629_7", "start": "2018-11-21T19:30:00.000Z", "startTimeMinutes": 1170, "end": "2018-11-21T23:00:00.000Z", "endTimeMinutes": 1380, "title": "Swimming lesson", "content": "<i class=\"w-icon material-icons\">pool</i>", "background": false, "allDay": false, "daysCount": 1, "class": "sport", "split": 2}, {"_eid": "2629_8", "start": "2018-11-23T12:30:00.000Z", "startTimeMinutes": 750, "end": "2018-11-23T13:00:00.000Z", "endTimeMinutes": 780, "title": "Macca's with Mark", "content": "<i class=\"w-icon material-icons\">fastfood</i>", "background": false, "allDay": false, "daysCount": 1, "class": "leisure", "split": 2}, {"_eid": "2629_9", "start": "2018-11-23T21:00:00.000Z", "startTimeMinutes": 1260, "end": "2018-11-23T23:30:00.000Z", "endTimeMinutes": 1410, "title": "Movie time", "content": "<i class=\"w-icon material-icons\">local_play</i>", "background": false, "allDay": false, "daysCount": 1, "class": "leisure", "split": 1}], "week": 47}
 19
 20
 21
 22
 23
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Doctor appointment
10:35 - 11:30
local_hospital
Dentist appointment
18:30 - 19:15
local_hospital
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
Swimming lesson
19:30 - 23:00
pool
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
<vue-cal
  selected-date="2018-11-19"
  :time-from="7 * 60"
  :time-to="23 * 60"
  :disable-views="['years', 'year']"
  hide-weekends
  editable-events
  :events="events"
  cell-contextmenu
  @ready="logEvents('ready', $event)"
  @view-change="logEvents('view-change', $event)"
  @cell-click="logEvents('cell-click', $event)"
  @cell-dblclick="logEvents('cell-dblclick', $event)"
  @cell-contextmenu="logEvents('cell-contextmenu', $event)"
  @cell-focus="logEvents('cell-focus', $event)"
  @event-focus="logEvents('event-focus', $event)"
  @event-mouse-enter="logEvents('event-mouse-enter', $event)"
  @event-mouse-leave="logEvents('event-mouse-leave', $event)"
  @event-title-change="logEvents('event-title-change', $event)"
  @event-content-change="logEvents('event-content-change', $event)"
  @event-duration-change="logEvents('event-duration-change', $event)"
  @event-drop="logEvents('event-drop', $event)"
  @event-create="logEvents('event-create', $event)"
  @event-drag-create="logEvents('event-drag-create', $event)"
  @event-delete="logEvents('event-delete', $event)">
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="7 * 60" :time-to="23 * 60" :disable-views="['years', 'year']" hide-weekends editable-events :events="events" cell-contextmenu @ready="logEvents('ready', $event)" @view-change="logEvents('view-change', $event)" @cell-click="logEvents('cell-click', $event)" @cell-dblclick="logEvents('cell-dblclick', $event)" @cell-contextmenu="logEvents('cell-contextmenu', $event)" @cell-focus="logEvents('cell-focus', $event)" @event-focus="logEvents('event-focus', $event)" @event-mouse-enter="logEvents('event-mouse-enter', $event)" @event-mouse-leave="logEvents('event-mouse-leave', $event)" @event-title-change="logEvents('event-title-change', $event)" @event-content-change="logEvents('event-content-change', $event)" @event-duration-change="logEvents('event-duration-change', $event)" @event-drop="logEvents('event-drop', $event)" @event-create="logEvents('event-create', $event)" @event-drag-create="logEvents('event-drag-create', $event)" @event-delete="logEvents('event-delete', $event)"> </vue-cal>

# External controls & use of Vue Cal methods

You can access any Vue Cal internal method through Vue refs.
This example shows how to control the Previous, Next and Today functions and the view selections from external buttons.
One important thing to notice is that you can use a v-model on the active-view (or :active-view.sync for Vue 2) in order to keep your variable updated when Vue Cal changes the view internally. For instance when you click the title to go to a broader view.
A v-model can also be used on the selected-date (or :selected-date.sync for Vue 2)

 24
 25
 26
 27
 28
 29
 30

Variables kept in sync thanks to v-model:

  • activeView: week,
  • selectedDate:
<button @click="activeView = 'day'">Day</button>
<button @click="activeView = 'week'">Week</button>
<button @click="activeView = 'month'">Month</button>
<button @click="activeView = 'year'">Year</button>
<button @click="activeView = 'years'">Years</button>
<br />
<button @click="$refs.vuecal.previous()">Previous</button>
<button @click="$refs.vuecal.switchView('day', new Date())">Today</button>
<button @click="$refs.vuecal.next()">Next</button>

<vue-cal
  ref="vuecal"
  v-model:active-view="activeView"
  v-model:selected-date="selectedDate"
  :time="false"
  hide-view-selector
  small>
</vue-cal>
<button @click="activeView = 'day'">Day</button> <button @click="activeView = 'week'">Week</button> <button @click="activeView = 'month'">Month</button> <button @click="activeView = 'year'">Year</button> <button @click="activeView = 'years'">Years</button> <br /> <button @click="$refs.vuecal.previous()">Previous</button> <button @click="$refs.vuecal.switchView('day', new Date())">Today</button> <button @click="$refs.vuecal.next()">Next</button> <vue-cal ref="vuecal" v-model:active-view="activeView" v-model:selected-date="selectedDate" :time="false" hide-view-selector small> </vue-cal>
Other useful Vue Cal internal methods & Date prototypes

Along with these Vue Cal internal methods that you can use externally, you can also call other useful Vue Cal methods.

  • switchToNarrowerView()

    Will drilldown the current view on selected date if there is a narrower view available.

  • minutesAtCursor(e)

    Will return the time (in minutes) at the cursor position when a DOM event occurs. `e` is the DOM event.

Useful Date prototypes

Don't miss out on these convenient functions! Read on in the Date prototypes section.

# Sync two vue-cal instances

In this example the right calendar is used as a date picker and the selected date is updated on the left calendar via the @cell-focus event listener.
To know more about emitted events refer to the emitted events example.

13 ( 2025)
 24
 25
 26
 27
 28
 29
 30
2025
24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
<vue-cal
  small
  :time="false"
  hide-view-selector
  active-view="week"
  :disable-views="['years', 'year', 'month']"
  :selected-date="selectedDate"
  class="vuecal--blue-theme"
  style="max-width: 360px;height: 260px">
</vue-cal>
<vue-cal
  xsmall
  :time="false"
  hide-view-selector
  active-view="month"
  :disable-views="['years', 'year', 'week', 'day']"
  @cell-focus="selectedDate = $event"
  class="vuecal--blue-theme vuecal--rounded-theme"
  style="max-width: 270px;height: 290px">
</vue-cal>
<vue-cal small :time="false" hide-view-selector active-view="week" :disable-views="['years', 'year', 'month']" :selected-date="selectedDate" class="vuecal--blue-theme" style="max-width: 360px;height: 260px"> </vue-cal> <vue-cal xsmall :time="false" hide-view-selector active-view="month" :disable-views="['years', 'year', 'week', 'day']" @cell-focus="selectedDate = $event" class="vuecal--blue-theme vuecal--rounded-theme" style="max-width: 270px;height: 290px"> </vue-cal>
data: () => ({
  selectedDate: null
})
data: () => ({ selectedDate: null })

# Modifying the array of events outside of Vue Cal

It is possible to modify the array of events like adding or removing an event after the first load, but be aware that by doing so all the events in Vue Cal will be replaced by the new array of events. You may lose your changes if you modified events within Vue Cal.

Here is the live array of event titles:

[
  "Doctor appointment",
  "Doctor appointment",
  "Doctor appointment",
  "Dentist appointment",
  "Crossfit",
  "Brunch with Jane",
  "Swimming lesson",
  "Macca's with Mark",
  "Movie time",
  "Another movie tonight",
  "Recall Dave",
  "Eat pop corns",
  "Enjoy the movie"
]
47 ( 2018)
 19
 20
 21
 22
 23
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
Doctor appointment
10:35 - 11:30
local_hospital
Dentist appointment
18:30 - 19:15
local_hospital
Crossfit
18:30 - 20:30
fitness_center
Brunch with Jane
11:00 - 13:00
local_cafe
Recall Dave
12:00 - 12:30
local_cafe
Swimming lesson
19:30 - 23:00
pool
Macca's with Mark
12:30 - 13:00
fastfood
Movie time
21:00 - 23:30
local_play
Eat pop corns
21:00 - 23:30
local_play
Enjoy the movie
21:00 - 23:30
local_play
<button
  @click="events.push({
    start: '2018-11-20 12:00',
    end: '2018-11-20 17:00',
    title: 'A new event',
    class: 'blue-event'
  })">Add an event</button>
<button @click="events.pop()">Remove last event</button>

<vue-cal
  selected-date="2018-11-19"
  :time-from="9 * 60"
  :time-to="23 * 60"
  :disable-views="['years', 'year', 'month']"
  hide-weekends
  :events="events">
</vue-cal>
<button @click="events.push({ start: '2018-11-20 12:00', end: '2018-11-20 17:00', title: 'A new event', class: 'blue-event' })">Add an event</button> <button @click="events.pop()">Remove last event</button> <vue-cal selected-date="2018-11-19" :time-from="9 * 60" :time-to="23 * 60" :disable-views="['years', 'year', 'month']" hide-weekends :events="events"> </vue-cal>
data: () => ({
  events: [
    {
      start: '2018-11-19 10:35',
      end: '2018-11-19 11:30',
      title: 'Doctor appointment',
      content: '<i class="icon material-icons">local_hospital</i>',
      class: 'health'
    },
    ...
  ]
})
data: () => ({ events: [ { start: '2018-11-19 10:35', end: '2018-11-19 11:30', title: 'Doctor appointment', content: '<i class="icon material-icons">local_hospital</i>', class: 'health' }, ... ] })

Advanced Vue Cal customization- when CSS won't do it

Here is the list of available slots:

  • title
  • arrow-prev
  • arrow-next
  • today-button
  • weekday-heading
  • split-label
  • time-cell
  • week-number-cell
  • cell-content
  • no-event
  • events-count
  • event

# Scroll the view to a particular time

It is quite easy to scroll to a particular time, and the user has the choice to add this outside of Vue Cal.

Bear in mind that IE11 needs a polyfill before you can use the scrollTo method on a DOM element, this single line will do.

// For IE11. Adds this to your page once (in `created` hook for instance).
if (!HTMLElement.prototype.scrollTo) HTMLElement.prototype.scrollTo = function ({ top }) { this.scrollTop = top }
// For IE11. Adds this to your page once (in `created` hook for instance). if (!HTMLElement.prototype.scrollTo) HTMLElement.prototype.scrollTo = function ({ top }) { this.scrollTop = top }

28, 2025
00:00
01:00
02:00
03:00
04:00
05:00
06:00
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
23:00
<vue-cal
  id="vuecal"
  :time-cell-height="timeCellHeight"
  @ready="scrollToCurrentTime">
</vue-cal>
<vue-cal id="vuecal" :time-cell-height="timeCellHeight" @ready="scrollToCurrentTime"> </vue-cal>
// `timeCellHeight` is set to 26 in the component data.
scrollToCurrentTime () {
  const calendar = document.querySelector('#vuecal .vuecal__bg')
  const hours = this.now.getHours() + this.now.getMinutes() / 60
  calendar.scrollTo({ top: hours * this.timeCellHeight, behavior: 'smooth' })
},
scrollToTop () {
  const calendar = document.querySelector('#vuecal .vuecal__bg')
  calendar.scrollTo({ top: 0, behavior: 'smooth' })
}
// `timeCellHeight` is set to 26 in the component data. scrollToCurrentTime () { const calendar = document.querySelector('#vuecal .vuecal__bg') const hours = this.now.getHours() + this.now.getMinutes() / 60 calendar.scrollTo({ top: hours * this.timeCellHeight, behavior: 'smooth' }) }, scrollToTop () { const calendar = document.querySelector('#vuecal .vuecal__bg') calendar.scrollTo({ top: 0, behavior: 'smooth' }) }

# Timeline tweaking

If you want to have more fancy time cells, you can override them with the time-cell-height option (in pixels) and scoped slots.
For even more flexibility, the horizontal lines are painted when you set the CSS class line on the tag you choose. So if you don't set this class you are free to paint the lines yourself or not.

5
15
30
45
6
15
30
45
7
15
30
45
8
15
30
45
9
15
30
45
10
15
30
45
11
15
30
45
12
15
30
45
13
15
30
45
14
15
30
45
15
15
30
45
16
15
30
45
17
15
30
45
18
15
30
45
19
15
30
45
20
15
30
45
21
15
30
45
22
15
30
45
23
15
30
45

If you are not familiar with scoped slots and destructuring slot-scope, you should first read about it: vuejs.org/guide/components/slots.htm

<vue-cal
  small
  :time-from="5 * 60"
  :time-step="15"
  :time-cell-height="18"
  active-view="day"
  :disable-views="['years', 'year', 'month']"
  hide-weekends>
  <template #time-cell="{ hours, minutes }">
    <div :class="{ 'vuecal__time-cell-line': true, hours: !minutes }">
      <strong v-if="!minutes" style="font-size: 15px">{{ hours }}</strong>
      <span v-else style="font-size: 11px">{{ minutes }}</span>
    </div>
  </template>
</vue-cal>
<vue-cal small :time-from="5 * 60" :time-step="15" :time-cell-height="18" active-view="day" :disable-views="['years', 'year', 'month']" hide-weekends> <template #time-cell="{ hours, minutes }"> <div :class="{ 'vuecal__time-cell-line': true, hours: !minutes }"> <strong v-if="!minutes" style="font-size: 15px">{{ hours }}</strong> <span v-else style="font-size: 11px">{{ minutes }}</span> </div> </template> </vue-cal>
.vuecal__time-cell-line.hours:before {border-color: #42b983;}
.vuecal__time-cell-line.hours:before {border-color: #42b983;}

# Custom events count

Using Vue.js scoped slots, you can also override the counting events method if you need.
If you are not familiar with scoped slots and destructuring slot-scope, you should first read about it: vuejs.org/guide/components/slots.htm

In the following example, we only count the events which have the custom leisure CSS class (orange color).

29
30
1
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
17
18
19
2
20
1
21
1
22
23
2
24
25
26
27
28
29
30
1
1
2
3
4
5
6
7
8
9
<vue-cal
  selected-date="2018-11-19"
  xsmall
  :time-from="10 * 60"
  :time-step="2 * 60"
  :disable-views="['day']"
  active-view="month"
  events-count-on-year-view
  :events="events">
  <template #events-count="{ events, view }">
    <span v-if="customEventsCount(events)">
      {{ customEventsCount(events) }}
    </span>
  </template>
</vue-cal>
<vue-cal selected-date="2018-11-19" xsmall :time-from="10 * 60" :time-step="2 * 60" :disable-views="['day']" active-view="month" events-count-on-year-view :events="events"> <template #events-count="{ events, view }"> <span v-if="customEventsCount(events)"> {{ customEventsCount(events) }} </span> </template> </vue-cal>

Alternatively, you could also use the cell-content slot instead of the events-count slot to perform the same task:
(Refer to the next example to know more: Custom title & cells)

<template #cell-content="{ cell, view, events }">
  <span class="vuecal__cell-date">
    {{ cell.content }}
  </span>
  <span
    class="vuecal__cell-events-count"
    v-if="['years', 'year', 'month'].includes(view.id) && customEventsCount(events)">
    {{ customEventsCount(events) }}
  </span>
</template>
<template #cell-content="{ cell, view, events }"> <span class="vuecal__cell-date"> {{ cell.content }} </span> <span class="vuecal__cell-events-count" v-if="['years', 'year', 'month'].includes(view.id) && customEventsCount(events)"> {{ customEventsCount(events) }} </span> </template>
// In your Vue component.
methods: {
  customEventsCount: events => {
    return events ? events.filter(e => e.class === 'leisure').length : 0
  }
}
// In your Vue component. methods: { customEventsCount: events => { return events ? events.filter(e => e.class === 'leisure').length : 0 } }
.vuecal__cell-events-count {background: transparent;}
.vuecal__cell-events-count span {
  background: #fd9c42;
  height: 100%;
  min-width: 12px;
  padding: 0 3px;
  border-radius: 12px;
  display: block;
}
.vuecal__cell-events-count {background: transparent;} .vuecal__cell-events-count span { background: #fd9c42; height: 100%; min-width: 12px; padding: 0 3px; border-radius: 12px; display: block; }

# Custom title & cells

Using Vue.js scoped slots, you can override the calendar main date title and calendar cells.
If you are not familiar with scoped slots and destructuring slot-scope, you should first read about it: vuejs.org/guide/components/slots.htm

Custom title

2 arguments are available through the scoped slot: #title="{ title, view }"

  • title, the formatted title (different on all the views). E.g."Week 2 (January 2019)"
  • view, an object containing the active view info.
    {
      id: {String}, // Current view, one of: years, year, month, week, day.
      startDate: {Date}, // JavaScript Date object.
      endDate: {Date}, // JavaScript Date object.
      selectedDate: {Date} // JavaScript Date object.
    }
    { id: {String}, // Current view, one of: years, year, month, week, day. startDate: {Date}, // JavaScript Date object. endDate: {Date}, // JavaScript Date object. selectedDate: {Date} // JavaScript Date object. }

You can use one or the other to format the title as you wish.
Using the pre-formatted title will be easy but not very flexible.
If you render the date yourself from view.startDate, don't forget the different formats for all the views: years, year, month, week, day.

Custom cells

In this example, only the cell number is clickable on month view.
5 arguments are available through the scoped slot:
#cell-content="{ cell, view, split, events, goNarrower }"

  • cell, object containing the cell date.
    {
      content: {String}, // Pre-formatted cell content if any.
      startDate: {Date}, // JavaScript Date object.
      endDate: {Date}, // JavaScript Date object.
      formattedDate: {String}, // formatted start date. E.g. "2019-04-05".
      today: {Boolean}
    }
    { content: {String}, // Pre-formatted cell content if any. startDate: {Date}, // JavaScript Date object. endDate: {Date}, // JavaScript Date object. formattedDate: {String}, // formatted start date. E.g. "2019-04-05". today: {Boolean} }
  • view, object containing the active view info.
    {
      id: {String}, // Current view, one of: years, year, month, week, day.
      startDate: {Date}, // JavaScript Date object.
      endDate: {Date}, // JavaScript Date object.
      selectedDate: {Date} // JavaScript Date object.
    }
    { id: {String}, // Current view, one of: years, year, month, week, day. startDate: {Date}, // JavaScript Date object. endDate: {Date}, // JavaScript Date object. selectedDate: {Date} // JavaScript Date object. }
  • split, when splitting days, object containing the current split info.
  • events, array containing all the events of the current cell or split.
  • goNarrower, function to navigate to narrower view if possible.

By default a cell is rendered as follows.
It is a good idea to reuse the same CSS classes as the different elements have associated styles:

<div class="vuecal__flex vuecal__cell-content">
<div class="vuecal__flex vuecal__cell-content">
Now this is the part you can customize:

<!-- Will be added if splitting days and split labels are set -->
<div class="split-label" />
<!-- Will be added on years, year & month view -->
<div class="vuecal__cell-date" />
<!-- Will be added on month view -->
<div class="vuecal__cell-events-count" />
<!-- Will be added on week and day view if no event -->
<div class="vuecal__no-event" />
Now this is the part you can customize: <!-- Will be added if splitting days and split labels are set --> <div class="split-label" /> <!-- Will be added on years, year & month view --> <div class="vuecal__cell-date" /> <!-- Will be added on month view --> <div class="vuecal__cell-events-count" /> <!-- Will be added on week and day view if no event --> <div class="vuecal__no-event" />
    <div class="vuecal__cell-events" />
</div>
<div class="vuecal__cell-events" /> </div>

24
25
26
27
28
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
6
<vue-cal
  :time="false"
  :dblclick-to-navigate="false"
  active-view="month"
  :events="events">

  <!-- Custom title -->
  <template #title="{ title, view }">
    🎉
    <span v-if="view.id === 'years'">Years</span>
    <!-- Using Vue Cal injected Date prototypes -->
    <span v-else-if="view.id === 'year'">{{ view.startDate.format('YYYY') }}</span>
    <span v-else-if="view.id === 'month'">{{ view.startDate.format('MMMM YYYY') }}</span>
    <span v-else-if="view.id === 'week'">w{{ view.startDate.getWeek() }} ({{ view.startDate.format('MMM YYYY') }})</span>
    <span v-else-if="view.id === 'day'">{{ view.startDate.format('dddd D MMMM (YYYY)') }}</span>
    🎉
  </template>

  <!-- Custom cells -->
  <template #cell-content="{ cell, view, events, goNarrower }">
    <span class="vuecal__cell-date" :class="view.id" v-if="view.id === 'day'" @click="goNarrower">
      {{ cell.date.getDate() }}
    </span>
    <span class="vuecal__cell-events-count" v-if="view.id === 'month' && events.length">{{ events.length }}</span>
    <span class="vuecal__no-event" v-if="['week', 'day'].includes(view.id) && !events.length">Nothing here 👌</span>
  </template>

  <!-- Alternatively to custom cells if you just want custom no-event text: -->
  <!-- <template #no-event>Nothing here 👌</template> -->
</vue-cal>
<vue-cal :time="false" :dblclick-to-navigate="false" active-view="month" :events="events"> <!-- Custom title --> <template #title="{ title, view }"> 🎉 <span v-if="view.id === 'years'">Years</span> <!-- Using Vue Cal injected Date prototypes --> <span v-else-if="view.id === 'year'">{{ view.startDate.format('YYYY') }}</span> <span v-else-if="view.id === 'month'">{{ view.startDate.format('MMMM YYYY') }}</span> <span v-else-if="view.id === 'week'">w{{ view.startDate.getWeek() }} ({{ view.startDate.format('MMM YYYY') }})</span> <span v-else-if="view.id === 'day'">{{ view.startDate.format('dddd D MMMM (YYYY)') }}</span> 🎉 </template> <!-- Custom cells --> <template #cell-content="{ cell, view, events, goNarrower }"> <span class="vuecal__cell-date" :class="view.id" v-if="view.id === 'day'" @click="goNarrower"> {{ cell.date.getDate() }} </span> <span class="vuecal__cell-events-count" v-if="view.id === 'month' && events.length">{{ events.length }}</span> <span class="vuecal__no-event" v-if="['week', 'day'].includes(view.id) && !events.length">Nothing here 👌</span> </template> <!-- Alternatively to custom cells if you just want custom no-event text: --> <!-- <template #no-event>Nothing here 👌</template> --> </vue-cal>

# Custom event rendering

Using Vue.js scoped slots, you can override the events rendering.

If you are not familiar with scoped slots and destructuring slot-scope, you should first read about it: vuejs.org/guide/components/slots.htm .

By default an event is rendered as follows.
It is a good idea to reuse the same CSS classes as the different elements have associated styles:

<div class="vuecal__event">
    <!-- Will be added if `editable-events` option is set to `true` -->
    <div class="vuecal__event-delete" />
<div class="vuecal__event"> <!-- Will be added if `editable-events` option is set to `true` --> <div class="vuecal__event-delete" />
Now this is the part you can customize:

<!-- Will be added if a title is set -->
<div class="vuecal__event-title" />
<!-- or if title is set and `editable-events` option is set to `true` -->
<div class="vuecal__event-title vuecal__event-title--edit" contenteditable />

<!-- Will be added if `time` option is set to `true` -->
<div class="vuecal__event-time" />

<!-- Will be added if a content is set -->
<div class="vuecal__event-content" />
Now this is the part you can customize: <!-- Will be added if a title is set --> <div class="vuecal__event-title" /> <!-- or if title is set and `editable-events` option is set to `true` --> <div class="vuecal__event-title vuecal__event-title--edit" contenteditable /> <!-- Will be added if `time` option is set to `true` --> <div class="vuecal__event-time" /> <!-- Will be added if a content is set --> <div class="vuecal__event-content" />
    <!-- Will be added if `editable-events` option is set to `true` -->
    <div class="vuecal__event-resize-handle" />
</div>
<!-- Will be added if `editable-events` option is set to `true` --> <div class="vuecal__event-resize-handle" /> </div>

Two parameters are passed through the scoped slot:

  • event: The event full object containing dates, time, title, content and custom attributes.
  • view: The current selected view id.

You can set any custom attribute you want on an event, they will then be accessible in your custom event renderer!
Note that _eid is a reserved keyword.

 19
 20
 21
 22
 23
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
Need to go shopping
Event start:2 O'clock
Event end:6 O'clock
Golf with John
Event start:10 O'clock
Event end:3 O'clock
<vue-cal
  selected-date="2018-11-19"
  :time-from="9 * 60"
  :time-to="19 * 60"
  hide-weekends
  :events="events">
  <template #event="{ event, view }">
    <v-icon>{{ event.icon }}</v-icon>

    <div class="vuecal__event-title" v-html="event.title" />
    <!-- Or if your events are editable: -->
    <div class="vuecal__event-title vuecal__event-title--edit"
         contenteditable
         @blur="event.title = $event.target.innerHTML"
         v-html="event.title" />

    <small class="vuecal__event-time">
      <!-- Using Vue Cal Date prototypes (activated by default) -->
      <strong>Event start:</strong> <span>{{ event.start.formatTime("h O'clock") }}</span><br/>
      <strong>Event end:</strong> <span>{{ event.end.formatTime("h O'clock") }}</span>
    </small>
  </template>
</vue-cal>
<vue-cal selected-date="2018-11-19" :time-from="9 * 60" :time-to="19 * 60" hide-weekends :events="events"> <template #event="{ event, view }"> <v-icon>{{ event.icon }}</v-icon> <div class="vuecal__event-title" v-html="event.title" /> <!-- Or if your events are editable: --> <div class="vuecal__event-title vuecal__event-title--edit" contenteditable @blur="event.title = $event.target.innerHTML" v-html="event.title" /> <small class="vuecal__event-time"> <!-- Using Vue Cal Date prototypes (activated by default) --> <strong>Event start:</strong> <span>{{ event.start.formatTime("h O'clock") }}</span><br/> <strong>Event end:</strong> <span>{{ event.end.formatTime("h O'clock") }}</span> </small> </template> </vue-cal>
events: [
  {
    start: '2018-11-20 14:00',
    end: '2018-11-20 18:00',
    title: 'Need to go shopping',
    icon: 'shopping_cart', // Custom attribute.
    class: 'leisure'
  },
  {
    start: '2018-11-22 10:00',
    end: '2018-11-22 15:00',
    title: 'Golf with John',
    icon: 'golf_course', // Custom attribute.
    class: 'sport'
  }
]
events: [ { start: '2018-11-20 14:00', end: '2018-11-20 18:00', title: 'Need to go shopping', icon: 'shopping_cart', // Custom attribute. class: 'leisure' }, { start: '2018-11-22 10:00', end: '2018-11-22 15:00', title: 'Golf with John', icon: 'golf_course', // Custom attribute. class: 'sport' } ]

# Custom day split labels

You can provide a custom split label when a simple label is not enough.

John
Tom
Kate
Jess
00:00
01:00
02:00
03:00
04:00
05:00
06:00
07:00
08:00
09:00
10:00
11:00
12:00
13:00
14:00
15:00
16:00
17:00
18:00
19:00
20:00
21:00
22:00
23:00
 
 
 
 
<vue-cal
  :disable-views="['years', 'year', 'month']"
  active-view="day"
  :split-days="daySplits"
  :hide-weekdays="[5, 6, 7]"
  sticky-split-labels>
  <template #split-label="{ split, view }">
    <i class="icon material-icons">person</i>
    <strong :style="`color: ${split.color}`">{{ split.label }}</strong>
  </template>
</vue-cal>
<vue-cal :disable-views="['years', 'year', 'month']" active-view="day" :split-days="daySplits" :hide-weekdays="[5, 6, 7]" sticky-split-labels> <template #split-label="{ split, view }"> <i class="icon material-icons">person</i> <strong :style="`color: ${split.color}`">{{ split.label }}</strong> </template> </vue-cal>
// In data.
customDaySplitLabels: [
  { label: 'John', color: 'blue', class: 'split1' },
  { label: 'Tom', color: 'green', class: 'split2' },
  { label: 'Kate', color: 'orange', class: 'split3' },
  { label: 'Jess', color: 'red', class: 'split4' }
]
// In data. customDaySplitLabels: [ { label: 'John', color: 'blue', class: 'split1' }, { label: 'Tom', color: 'green', class: 'split2' }, { label: 'Kate', color: 'orange', class: 'split3' }, { label: 'Jess', color: 'red', class: 'split4' } ]
.vuecal .day-split-header {font-size: 11px;}
.vuecal__body .split1 {background-color: rgba(226, 242, 253, 0.7);}
.vuecal__body .split2 {background-color: rgba(232, 245, 233, 0.7);}
.vuecal__body .split3 {background-color: rgba(255, 243, 224, 0.7);}
.vuecal__body .split4 {background-color: rgba(255, 235, 238, 0.7);}
.vuecal__no-event {display: none;}
.vuecal .day-split-header {font-size: 11px;} .vuecal__body .split1 {background-color: rgba(226, 242, 253, 0.7);} .vuecal__body .split2 {background-color: rgba(232, 245, 233, 0.7);} .vuecal__body .split3 {background-color: rgba(255, 243, 224, 0.7);} .vuecal__body .split4 {background-color: rgba(255, 235, 238, 0.7);} .vuecal__no-event {display: none;}

API

Here is the list of all the available views.

['years', 'year', 'month', 'week', 'day']
['years', 'year', 'month', 'week', 'day']

Here is the list of all the parameters available and their description below this table.
Remember that HTML is case-insensitive and you should therefore use the kebab-case instead of the camelCase for consistency.

activeView:             [String],          default: 'week'
allDayBarHeight:        [String, Number],  default: '25px'
cellClickHold:          [Boolean],         default: true
cellContextmenu:        [Boolean],         default: false
clickToNavigate:        [Boolean],         default: false
dblclickToNavigate:     [Boolean],         default: true
disableDatePrototypes:  [Boolean],         default: false
disableDays:            [Array],           default: []
disableViews:           [Array],           default: []
dragToCreateEvent:      [Boolean],         default: true
dragToCreateThreshold:  [Number],          default: 15
editableEvents:         [Boolean, Object], default: false
events:                 [Array],           default: []
eventsCountOnYearView:  [Boolean],         default: false
eventsOnMonthView:      [Boolean, String], default: false
hideBody:               [Boolean],         default: false
hideTitleBar:           [Boolean],         default: false
hideViewSelector:       [Boolean],         default: false
hideWeekdays:           [Array],           default: []
hideWeekends:           [Boolean],         default: false
locale:                 [String],          default: 'en'
maxDate:                [String, Date],    default: ''
minCellWidth:           [Number],          default: 0 // In pixels.
minDate:                [String, Date],    default: ''
minEventWidth:          [Number],          default: 0 // In percent.
minSplitWidth:          [Number],          default: 0 // In pixels.
onEventClick:           [Function],        default: null
onEventCreate:          [Function],        default: null
onEventDblclick:        [Function],        default: null
overlapsPerTimeStep:    [Boolean],         default: false
resizeX:                [Boolean],         default: false
selectedDate:           [String, Date],    default: ''
showAllDayEvents:       [Boolean, String], default: false
showTimeInCells:        [Boolean],         default: false
showWeekNumbers:        [Boolean, String], default: false
small:                  [Boolean],         default: false
snapToTime:             [Number],          default: null
specialHours:           [Object],          default: {}
splitDays:              [Array],           default: []
startWeekOnSunday:      [Boolean],         default: false
stickySplitLabels:      [Boolean],         default: false
time:                   [Boolean],         default: true
timeCellHeight:         [Number],          default: 40 // In pixels.
timeFormat:             [String],          default: ''
timeFrom:               [Number],          default: 0 // In minutes.
timeStep:               [Number],          default: 60 // In minutes.
timeTo:                 [Number],          default: 24 * 60 // In minutes.
todayButton:            [Boolean],         default: false
transitions:            [Boolean],         default: true
twelveHour:             [Boolean],         default: false
xsmall:                 [Boolean],         default: false
watchRealTime:          [Boolean],         default: false
activeView: [String], default: 'week' allDayBarHeight: [String, Number], default: '25px' cellClickHold: [Boolean], default: true cellContextmenu: [Boolean], default: false clickToNavigate: [Boolean], default: false dblclickToNavigate: [Boolean], default: true disableDatePrototypes: [Boolean], default: false disableDays: [Array], default: [] disableViews: [Array], default: [] dragToCreateEvent: [Boolean], default: true dragToCreateThreshold: [Number], default: 15 editableEvents: [Boolean, Object], default: false events: [Array], default: [] eventsCountOnYearView: [Boolean], default: false eventsOnMonthView: [Boolean, String], default: false hideBody: [Boolean], default: false hideTitleBar: [Boolean], default: false hideViewSelector: [Boolean], default: false hideWeekdays: [Array], default: [] hideWeekends: [Boolean], default: false locale: [String], default: 'en' maxDate: [String, Date], default: '' minCellWidth: [Number], default: 0 // In pixels. minDate: [String, Date], default: '' minEventWidth: [Number], default: 0 // In percent. minSplitWidth: [Number], default: 0 // In pixels. onEventClick: [Function], default: null onEventCreate: [Function], default: null onEventDblclick: [Function], default: null overlapsPerTimeStep: [Boolean], default: false resizeX: [Boolean], default: false selectedDate: [String, Date], default: '' showAllDayEvents: [Boolean, String], default: false showTimeInCells: [Boolean], default: false showWeekNumbers: [Boolean, String], default: false small: [Boolean], default: false snapToTime: [Number], default: null specialHours: [Object], default: {} splitDays: [Array], default: [] startWeekOnSunday: [Boolean], default: false stickySplitLabels: [Boolean], default: false time: [Boolean], default: true timeCellHeight: [Number], default: 40 // In pixels. timeFormat: [String], default: '' timeFrom: [Number], default: 0 // In minutes. timeStep: [Number], default: 60 // In minutes. timeTo: [Number], default: 24 * 60 // In minutes. todayButton: [Boolean], default: false transitions: [Boolean], default: true twelveHour: [Boolean], default: false xsmall: [Boolean], default: false watchRealTime: [Boolean], default: false
  • locale[String], default: 'en'

    Allows you to translate the calendar texts in a given language.
    Use a 2 letter locale code (ISO 639-1) unless a distinction is needed. E.g. 'pt-br' for Portuguese-Brasilian.

    Currently available languages are Albanian, Arabic, Bangla, Bosnian, Bulgarian, Catalan, Czech, Chinese (Simplified), Chinese (Traditional), Croatian, Danish, Dutch, English, Estonian, Farsi, French, Georgian, German, Greek, Hebrew, Hungarian, Icelandic, Italian, Indonesian, Japanese, Korean, Lithuanian, Mongolian, Norwegian, Polish, Portuguese Brasilian, Romanian, Russian, Serbian, Slovak, Slovenian, Spanish, Swedish, Turkish, Ukrainian, Vietnamese.
    If you are interested in providing a language support please do a pull request with a json file into the i18n directory.
    this is what a language json looks like.

    {
      "weekDays": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
      "months": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
      "years": "Years",
      "year": "Year",
      "month": "Month",
      "week": "Week",
      "day": "Day",
      "today": "Today",
      "noEvent": "No Event",
      "allDay": "All day",
      "deleteEvent": "Delete",
      "createEvent": "Create an event",
      "dateFormat": "dddd D MMMM YYYY"
    }
    { "weekDays": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], "months": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], "years": "Years", "year": "Year", "month": "Month", "week": "Week", "day": "Day", "today": "Today", "noEvent": "No Event", "allDay": "All day", "deleteEvent": "Delete", "createEvent": "Create an event", "dateFormat": "dddd D MMMM YYYY" }

    Regarding the dateFormat translation, this is the format of the full date you can see in a single day view title.
    dddd stands for the full-letter day of week, MMMM stands for full-letter month, D stands for the date of the month (0-31), YYYY stands for full year, {S} stands for st/nd/rd/th and only in English.

    Note that 2 media queries will shorten the days of the week to 3 letters then 1 letter when it does not fit.
    You can read more about it in the # Responsiveness & Media Queries section in the CSS Notes.

  • hideViewSelector[Boolean], default: false

    When set to true, the top view selector will disappear.
    You can still navigate from a view to another by clicking a cell (narrower view) or the view title (broader view).

  • hideTitleBar[Boolean], default: false

    When set to true, the title bar with navigating arrows will disappear.
    You can still navigate from a view to another with the view selector and clicking a cell (narrower view).

  • hideBody[Boolean], default: false

    When set to true, the whole calendar body will disappear - cells and timeline.
    Also means that all the logic usually triggered from the calendar's body won't run at all.

  • hideWeekends[Boolean], default: false

    Hide the weekend and shows only Monday to Friday on month view and week view.
    The weekend are still visible in day view not to break the behavior of the arrows.
    Note that by hiding the arrows you won't be able to see a weekend day in day view if hideWeekends is true.

  • hideWeekdays[Array], default: []

    Hide particular days of the week. This option accepts an array of days (day numbers) to hide, starting at 1 for Monday, to 7 for Sunday.
    This option will apply on month & week views.

    If you want to hide Saturday and Sunday you can put 6, 7 in the array or use hideWeekends in supplement of hideWeekdays.

  • disableDays[Array], default: []

    Allows you to provide an array of formatted dates (e.g. 2020-09-18) to disable.

  • disableViews[Array], default: []

    Allows you to totally disable one or more of the available views.
    Accepted view names are 'years', 'year', 'month', 'week', 'day'.
    Note that the navigation between views via cells click or title click won't break and will only navigate to views you have allowed.

  • dragToCreateEvent[Boolean], default: true

    When events are editable and if time and dragToCreateEvent are set to true, clicking and dragging on a cell will create an event.
    Note: if this option is set to true, it will prevent event creation from cell click & hold.
    Refer to the Create events example.

  • dragToCreateThreshold[Number], default: 15

    When events are editable and time and dragToCreateEvent are set to true, this option controls the minimum dragging distance before an event is created.
    This option might be useful when you can navigate with cell click to prevent unwanted event creation in case of slipping cursor while clicking.
    With option gets to a positive integer, and you can set it to 0 to disable it. Refer to the Create events example.

  • activeView[String], default: 'week'

    Allows you to set a default active view, for the first time you load the calendar.
    Then control the active view from outside of Vue Cal.
    Accepts one of 'years', 'year', 'month', 'week', 'day'.
    The active view has a two-way binding: you can use a v-model (or .sync on Vue 2) on it to keep your variable up to date.

  • allDayBarHeight[String, Number], default: '25px'

    When the all day bar is visible and Vue Cal is also scrollable horizontally (due to minCellWidth or day splits with minSplitWidth), the all-day bar must have a fixed height for this particular layout.
    Only if these conditions are fulfilled, the height provided through this option will be used. If none is provided the default height will be used.
    The height can be any valid CSS height (as a string) or an integer for an amount of pixels.

  • todayButton[Boolean], default: false

    Adds a Today button in the title bar to quickly go to Today's date.

  • showAllDayEvents[Boolean, String], default: false
    • When the showAllDayEvents is set to true the events with an allDay attribute set to true will be displayed in a fixed top bar on the week & day views.
      The all day events bar will only show up if the options showAllDayEvents & time are set to true.
      time is important since without time information every event is an all-day event there is no point in separating them then.
    • When showAllDayEvents is set to false, all the all day events (allDay attribute set to true), will show up as a normal background event.
    • On month view, switching showAllDayEvents on and off will not have any impact since both should display the all day events.
    • showAllDayEvents accepts a Boolean or the string 'short', to display only the event title.
  • showWeekNumbers[Boolean], default: false

    When set to true, the weeks numbers will show in the first column on the month view (only).
    You can also provide a custom renderer to the weeks numbers cells through the week-number-cell slot.

    Did you know there can be 53 weeks in the year?
    This happens every time the year starts a Thursday, or starts a Wednesday of a leap year. In this case the week number will be 53 instead of 1.

  • selectedDate[String, Date], default: ''

    Accepts a formatted string or plain JS Date object.
    Set a selected date, for the first time you load the calendar.
    This day will be highlighted and the first view will naturally show this date.
    E.g. setting a date in year 2000 with a activeView of week, will show you that week of year 2000.

    Updating the selectedDate programmatically after the first calendar load, will update the view if needed to show this date.
    Refer to the Sync two vue-cal instances example.
    The selected date has a two-way binding: you can use a v-model (or .sync on Vue 2) on it to keep your variable up to date.

    A correct string date format is 2025-03-28 22:59 or 2025-03-28 if you don't need the time. Only these formats will work as a string. You can also provide a native Javascript Date object.

  • minDate[String, Date], default: ''

    Accepts a formatted string or plain JS Date object.
    Set a minimum date for the cells to be selectable.
    By default the cell will be grayed out when out of range but CSS classes let you customize this.

  • maxDate[String, Date], default: ''

    Accepts a formatted string or plain JS Date object.
    Set a maximum date for the cells to be selectable.
    By default the cell will be grayed out when out of range but CSS classes let you customize this.

  • specialHours[Object], default: {}

    Allows an individual highlighted time range for each day of the week.
    For instance, it could represent the business hours.
    The object must contain indexed days, from 1 for Monday to 7 for Sunday, of the days you want to highlight.
    Each day must contain an object with a from and to properties defining the beginning and the end of the time range in minutes.
    In addition, you can set a CSS class for each day of the week.
    It is also possible to provide an array of special hours for the same day.
    A label can also be provided per special hour block, and styled via CSS.

    Example for Wednesday: :special-hours="specialHours"

    With a single range of special hours:

    // In the component's data.
    specialHours: {
      3: { from: 8 * 60, to: 20 * 60, class: 'open' }
    }
    // In the component's data. specialHours: { 3: { from: 8 * 60, to: 20 * 60, class: 'open' } }

    With multiple ranges of special hours:
    // In the component's data.
    specialHours: {
      3: [
        { from: 8 * 60, to: 12 * 60, class: 'open' },
        { from: 14 * 60, to: 20 * 60, class: 'open' }
      ]
    }
    
    // In the component's data. specialHours: { 3: [ { from: 8 * 60, to: 12 * 60, class: 'open' }, { from: 14 * 60, to: 20 * 60, class: 'open' } ] }

  • startWeekOnSunday[Boolean], default: false

    By default weeks start on Monday but with this option you can start the week on Sunday.

  • small[Boolean], default: false

    When set to true, the days of the week headings will be truncated to 3 letters.
    Does not apply to the title of the day view.
    2 media queries are truncating the days of the week below 450px, read on in the CSS Notes.

  • xsmall[Boolean], default: false

    When set to true, the days of the week headings will be truncated to 1 letter.
    Does not apply to the title of the day view.
    In Addition, the whole calendar gets applied a smaller font size of 0.9em, and the current view title is also reduced.
    2 media queries are truncating the days of the week below 450px, read on in the CSS Notes.

  • transitions[Boolean], default: true

    Enable / disable the CSS transitions between all the views and view states.

  • clickToNavigate[Boolean], default: false

    When set to true a single click (or tap for touch devices) will take you to a narrower view if available.
    You can always go back to a broader view by clicking the view title or selecting another view from the view selector if enabled.
    The navigation to narrower view can be disabled by setting both clickToNavigate and dblclickToNavigate to false.

  • dblclickToNavigate[Boolean], default: true

    When set to true a double click (or double tap for touch devices) will take you to a narrower view if available.
    You can always go back to a broader view by clicking the view title or selecting another view from the view selector if enabled.
    The navigation to narrower view can be disabled by setting both clickToNavigate and dblclickToNavigate to false.

  • cellClickHold[Boolean], default: true

    Allows you to disable the default event creation on cell click & hold which only happens if editableEvents.create is set to true.

  • cellContextmenu[Boolean], default: false

    When set to true, a right click on a cell will emit the cell-contextmenu event, providing an object containing: the date and time at cursor, the x and y position of cursor, and the full original DOM event.

  • time[Boolean], default: true

    Whether you want to display the timeline and handle events with time or only date.
    Note that time is made of hours:minutes and no second.

  • timeFrom[Number], default: 0

    If time is enabled, set the start of the timeline in minutes. By default it starts at midnight.

  • showTimeInCells[Boolean], default: false

    When set to true, the time labels will be visible in each cell, in each time slot of the day and week views. You can then use CSS to style to taste. For instance, you could hide all the labels and show only the one that is being hovered.
    Will have no effect if time is set to false.

  • timeTo[Number], default: 24 * 60

    If time is enabled, set the end of the timeline in minutes. By default it ends at 24:00.

  • timeStep[Number], default: 60

    If time is enabled, set the time increment in minutes.

  • timeCellHeight[Number], default: 40

    If time is enabled, set the time cell height in pixels.
    this is very important as it is used to calculate the events position in the day.

  • twelveHour[Boolean], default: false

    If time is enabled, the default time format is 24 hour.
    With twelveHour set to true (use twelve-hour in template), the time format will show 12 hours suffixed with am/pm.

  • timeFormat[String], default: ''

    When defined, overrides the default time format in time cells and events.
    Formatted time can contain any character but the following characters will be replaced:

    • H: Hours no leading zero, 24-hour format
    • HH: Hours with leading zero, 24-hour format
    • h: Hours no leading zero, 12-hour format
    • hh: Hours with leading zero, 12-hour format
    • m: Minutes no leading zero
    • mm: Minutes with leading zero
    • {am}: am or pm
    • The characters `{` and `}` are removed and used only to delimit keywords when there is no space.
      E.g. "h:mm{am}".
  • watchRealTime[Boolean], default: false

    When set to true, the current time line in today's cell, on week and day views, will stay in sync with real time.
    (This requires a setTimeout every minute)

  • minEventWidth[Number], default: 0

    When a number is set, in percent, each event within a cell will have a minimum width.
    If the provided percentage is bigger than what it would naturally be, the events will partially overlap.

  • overlapsPerTimeStep[Boolean], default: false

    When set to true, each event of the same cell will have a width of 100% / [number of simultaneous events] only if these events are within the same time step.
    Refer to this use case.

  • minCellWidth[Number], default: 0

    When a number is set, in pixels, each cell of the week view (only) will have this minimum width.
    If it does not fit in the calendar body, the overflow will be scrollable. If minSplitWidth is also set, it will override minCellWidth.

  • minSplitWidth[Number], default: 0

    This is for day splits only, and it applies to the week and day views (only).
    When a number is set, in pixels, each split of each cell will have this minimum width.
    If it does not fit in the calendar body, the overflow will be scrollable.
    If minCellWidth is also set, minSplitWidth will override it on week view.

  • splitDays[Array], default: []

    Split each day into multiple vertical splits.
    Accepts an array of split objects with attributes.
    Each split object can have these attributes, they are all optional:

    {
      id: {Integer | String}, // All ids must be set if using `hide`.
      class: {String},
      label: {String},
      hide: {Boolean} // You can toggle the column on and of with this.
    }
    { id: {Integer | String}, // All ids must be set if using `hide`. class: {String}, label: {String}, hide: {Boolean} // You can toggle the column on and of with this. }

  • stickySplitLabels[Boolean], default: false

    When set to true, the day splits labels will be displayed in the header instead of in-cell.

  • editableEvents[Boolean, Object], default: false

    When editableEvents is set to true, it allows:

    • Dragging and dropping events
    • Resizing events by dragging the handle showing at the bottom of each event if time is set to true,
    • Deleting events by click and hold an event.
    • Editing events title

    • You can set more accurately which edition you want to allow by passing an object.
      For instance, this object will allow all the above editions except the drag & drop:
      { title: true, drag: false, resize: true, delete: true, create: true }
    • You can still force an event to be undeletable or unresizable from the deletable & resizable event attributes.

  • resizeX[Boolean], default: false

    When set to true, allows resizing an event across multiple days.
    Resizing on the X axis is only available on week view.

  • snapToTime[Number], default: 0

    Accepts a number of minutes from 0 to 60 to snap a dropped event or an event end time while resizing.
    For instance, with a snapToTime of 15 min, an event dropped at a start of 10:05, will snap to 10:00, and dropped at 10:11 will snap to 10:15.
    This option affects event resizing, event drag & dropping, and event drag-creation.

  • eventsOnMonthView[Boolean, String], default: false

    When set to true, the events will also be displayed on month view (including events from visible out of scope days).
    When set to the string 'short', only the event's title will be displayed.

  • eventsCountOnYearView[Boolean], default: false

    When set to true, the events count will also be displayed on years & year views.

  • onEventClick[Function], default: null

    A callback function to execute when an event is clicked.
    this function receives 2 parameters: event, the clicked calendar event, and e, the associated JavaScript DOM event.

  • onEventDblclick[Function], default: null

    A callback function to execute when an event is double clicked.
    this function receives 2 parameters: event, the double clicked calendar event, and e, the associated JavaScript DOM event.

  • onEventCreate[Function], default: null

    A callback function to execute when an event is created.
    This function receives 2 parameters: event, the created event, and deleteEvent, a function to delete the created event.
    You can modify and override the received event and return it to vue-cal.
    If this function returns false, the event creation will be cancelled.

  • events[Array], default: []

    Allows you to place events in the calendar.
    Accepts an array of event objects.
    This is what an event object must look like:

    {
      start: '2018-11-19 12:00', // Required.
      end: '2018-11-19 14:00', // Required.
      // Instead of formatted dates, you can also provide Javascript Date objects:
      // start: new Date(2018, 11 - 1, 19, 12, 0),
      // end: new Date(2018, 11 - 1, 19, 14, 0),
      title: {String}, // Optional.
      content: {String}, // Optional.
      class: {String}, // Optional - space-separated css classes.
      background: {Boolean} // Optional. (Event type not CSS property)
      split: {Number|String} // Optional.
      allDay: {Boolean} // Optional.
      deletable: false // optional - force undeletable when events are editable.
      resizable: false // optional - force unresizable when events are editable.
    }
    { start: '2018-11-19 12:00', // Required. end: '2018-11-19 14:00', // Required. // Instead of formatted dates, you can also provide Javascript Date objects: // start: new Date(2018, 11 - 1, 19, 12, 0), // end: new Date(2018, 11 - 1, 19, 14, 0), title: {String}, // Optional. content: {String}, // Optional. class: {String}, // Optional - space-separated css classes. background: {Boolean} // Optional. (Event type not CSS property) split: {Number|String} // Optional. allDay: {Boolean} // Optional. deletable: false // optional - force undeletable when events are editable. resizable: false // optional - force unresizable when events are editable. }

    • If no title is provided, no title will be displayed.
    • content accepts free HTML, for instance: '<i class="icon material-icons">local_hospital</i>'.
      If no content is provided, no content will be displayed.
    • You may need an event CSS class to handle different event types for instance. With different classes you can apply different styles to the events.
      E.g. backgrounds, images, borders, etc.
    • The background attribute sets an event as a background event, which allows overlapping and disable the ability to drag & resize.
    • When using splitDays, the split attribute accepts a number, starting from 1, corresponding to the split you want the event to appear in.
      Optionally, if you have set the id property in splitDays, you have to use the same id here (Integer or String).
    • When the showAllDayEvents and time options are set to true, all the events with an attribute allDay set to true will show up in a fixed bar (week & day views).

    Important notes

    • The events are internally identified by the key `_eid`. This is a reserved keyword.
    • Correct date formats are 2025-03-28 22:59, or 2025-03-28 if you don't want any time in the whole calendar, or a JavaScript Date object. Only these formats will work.
      You can't mix events with time and events without, and you can only remove time if the time option is set to false.
    • You can set an event end at 24:00 or 00:00 (for the next midnight), but internally the date will be set at 23:59:59 so the date stays the same instead of natural behavior of taking the next day at 00:00:00.
      When returned from emitted events, this event end will contain a date ending at 23:59:59.

  • disableDatePrototypes[Boolean], default: false

    If you really don't want the Date prototypes to be added, you can disable them with this option.
    Refer to This Vue Cal issue on Github.

Date Prototypes

Vue Cal has no dependency and performs date operations through a few notable useful and efficient functions that have been added to the native Date class for your convenience.
With this set of functions, you will most likely not need Moment.js or any other additional Date library!

Once Vue Cal is loaded, you can access the following functions from anywhere in your code just like a simple Date function.
E.g. (new Date()).addDays(2)

  • .addDays(days)Adds days to a Date object and returns it. The original Date stays untouched as a copy is made.
    `days` is an integer.
  • .subtractDays(days)Subtracts days to a Date object and returns it. The original Date stays untouched as a copy is made.
    `days` is an integer.
  • .addHours(hours)Adds hours to a Date object and returns it. The original Date stays untouched as a copy is made.
    `hours` is an integer.
  • .subtractHours(hours)Subtracts hours to a Date object and returns it. The original Date stays untouched as a copy is made.
    `hours` is an integer.
  • .addMinutes(minutes)Adds minutes to a Date object and returns it. The original Date stays untouched as a copy is made.
    `minutes` is an integer.
  • .subtractMinutes(minutes)Subtracts minutes to a Date object and returns it. The original Date stays untouched as a copy is made.
    `minutes` is an integer.
  • .getWeek()Returns the week number (1 to 53) of a date.
  • .isToday()Returns true if the date is Today.
  • .isLeapYear()Returns true if the date is in a leap year.

And because everyone needs a Date/time formatting function...

It is now available directly from the Date object, with your loaded locale!

  • .format(format)
    Returns a formatted date string. Default format is 'YYYY-MM-DD', but you can use any formatting keyword from this list, and add any character not present in this mapping:
    • YYYY: full year. // `2019`
    • YY: 2 last digits of the year. // `19`
    • MMMM: month in full. // `January`
    • MMM: 3 first letters of the month. // `Jan`
    • MM: month number with leading zero. (01-12) // `01`
    • M: month number without leading zero. (1-12) // `1`
    • DD: date of the month with leading zero. (01-31) // `01`
    • D: date of the month without leading zero. (1-31) // `1`
    • S: (usually with surrounding `{ }`) only in English, will output `st`, `nd`, `rd` or `th`.
    • dddd: day of the week in full. // `Monday`
    • ddd: 3 first letters of the day of the week. // `Mon`
    • dd: first letter of the day of the week. // `M`
    • d: number of the day of the week. (1-7) // `1` for Monday
    • And also all the keywords from the following formatTime() function.
  • .formatTime(format)
    Returns a formatted time string.
    The format() function can also do this, but this might be a shortcut if you just want the default time formatting.
    E.g. `formatTime()`).
    This function will also be slightly faster than format() as lighter in functionality.
    Default format is 'HH:mm', but you can use any formatting keyword from this list, and add any character not present in this mapping:
    • HH: Hours with leading zero, 24-hour format. (00-24)// `20`
    • H: Hours without leading zero, 24-hour format. (0-24)// `20`
    • hh: Hours with leading zero, 12-hour format. // `08`
    • h: Hours without leading zero, 12-hour format. // `8`
    • mm: Minutes with leading zero. // `08`
    • m: Minutes without leading zero. // `8`
    • am: (usually with surrounding `{ }`) am or pm (also localized if any)

  • To separate 2 keywords or a keyword and another text not from this list without adding spaces or any separation, you can use the delimiters `{ }`.
    For instance `new Date().format('YYYY{MM}DD')` (or even `{YYYY}{MM}{DD}`) will produce: "20250328".
  • The Date functions are added when Vue Cal loads, you can always check if you have it before you use it:
    Date.prototype.format && new Date().format()
  • If you really don't want the Date prototypes to be added, you can disable them with this option: disable-date-prototypes.
    Refer to This Vue Cal issue on Github.

CSS Notes

You can easily change the calendar design with CSS.

# Color themes & rounded theme

Currently 2 color themes (green & blue) are available, in addition to the standard grey theme.
You can apply a green or blue theme by using the CSS class vuecal--green-theme or vuecal--blue-theme.

If you want another color theme, you can define your own easily.
This is what a standard color theme looks like. You can copy and change any color to quickly get a nice render.
If that is still not doing what you want you can change even more in your own CSS.

/* Green-theme. */
.vuecal__menu, .vuecal__cell-events-count {background-color: #42b983;}
.vuecal__title-bar {background-color: #e4f5ef;}
.vuecal__cell--today, .vuecal__cell--current {background-color: rgba(240, 240, 255, 0.4);}
.vuecal:not(.vuecal--day-view) .vuecal__cell--selected {background-color: rgba(235, 255, 245, 0.4);}
.vuecal__cell--selected:before {border-color: rgba(66, 185, 131, 0.5);}
/* Cells and buttons get highlighted when an event is dragged over it. */
.vuecal__cell--highlighted:not(.vuecal__cell--has-splits),
.vuecal__cell-split--highlighted {background-color: rgba(195, 255, 225, 0.5);}
.vuecal__arrow.vuecal__arrow--highlighted,
.vuecal__view-btn.vuecal__view-btn--highlighted {background-color: rgba(136, 236, 191, 0.25);}
/* Green-theme. */ .vuecal__menu, .vuecal__cell-events-count {background-color: #42b983;} .vuecal__title-bar {background-color: #e4f5ef;} .vuecal__cell--today, .vuecal__cell--current {background-color: rgba(240, 240, 255, 0.4);} .vuecal:not(.vuecal--day-view) .vuecal__cell--selected {background-color: rgba(235, 255, 245, 0.4);} .vuecal__cell--selected:before {border-color: rgba(66, 185, 131, 0.5);} /* Cells and buttons get highlighted when an event is dragged over it. */ .vuecal__cell--highlighted:not(.vuecal__cell--has-splits), .vuecal__cell-split--highlighted {background-color: rgba(195, 255, 225, 0.5);} .vuecal__arrow.vuecal__arrow--highlighted, .vuecal__view-btn.vuecal__view-btn--highlighted {background-color: rgba(136, 236, 191, 0.25);}

Rounded theme
You can use the rounded cells theme like in the Example Calendar themes - Rounded cells, by applying the CSS class vuecal--rounded-theme to the Vue Cal wrapper.

# Responsiveness & media queries

This calendar is fully responsive.
To help you in making the calendar always look perfect, 2 media queries (to keep it simple) are in place for small screens.
The media queries operate downwards from 550px & 450px, to truncate the text of the days of the week from full day name to 3 letters and to 1 letter according to the available space.

If this is not enough for your particular use, you can add your own in your CSS.
Additionally, you can use the options small & xsmall to truncate week days at any size.

# Disabled text selection

Notice that on Windows machines, and on devices where the scrollbar takes some space and pushes the calendar main content (week and day views), the weekdays headings and the all-day bar get an added padding right to align well with the main content.
This is done once, in the mounted hook.

# Disabled text selection

By default the selection is disabled in the whole calendar except in the events. you can override this by CSS.

Release notes

Version 4.10.0Merged PRs #585, #536, #528: fixed week start date when starting week on Sunday, fixed maxTimestamp, fixed date format on `en` locale.
Version 4.9.0Added the Portuguese (Portugal) and Finnish locale.
Version 4.8.0Added the Estonian locale.
Version 4.6.0Skip the hidden days on day view (days listed in hideWeekends and hideWeekdays).
Version 4.5.1Fix wrong starting day on week view when the week starts on sunday & hideWeekends is true.
Version 4.5.0Added option to display time labels in cells.
Version 4.4.3Fix headings misalignments on certain views, when OS sets fixed scrollbars.
Version 4.4.0Added a v-model on the selected-date.
Version 4.3.4New ESM build by default.
Version 4.0.0Supports and only works on Vue 3.
Version 3.10.1Prevent focusing cells when they are disabled.
Version 3.10.0Allow multiple ranges in daily special hours.
Version 3.9.1Fix: Prevent disabling full month or year when using disable-days.
Version 3.9.0Allow full custom locale.
Version 3.8.4Fix showing events ending at 24:00 on Safari.
Version 3.8.3Return the full original DOM event from cell-contextmenu.
Version 3.8.2Add active-view validations and raise warning if incorrect.
Version 3.8.1Fix all-day events when no time information is provided.
Version 3.8.0Add Mongolian language.
Version 3.7.0Add a new disable-days option.
Version 3.6.5Call the on-event-click function (if any) on event single tap. Also add a 30px threshold to not call the handler if the event was tapped and dragged.
Version 3.6.4
  • Fix events ending at 00:00, now ends at 23.59.59 of the previous day or of the same day if time is false.
  • When time is true, keep 23:59:59 internally, but format string to 24:00.
  • Fix previous / next navigation on day view when date prototypes are disabled.
  • Add shortWeekDays to the uk locale.
Version 3.6.3Return the correct event on out-of-scope event click on month view.
Version 3.6.2Fix all-day events not showing up and fix error when using hide-body.
Version 3.6.1Fix timeless events not showing up.
Version 3.6.0Added Albanian language and short days in Russian language
Version 3.5.6Fix multiple day events display across days when time is false
Version 3.5.4Add mouseup event listener when deletable but not resizable
Version 3.5.3
  • Provide the split (if any) to event creation from cell click & hold.
  • Add mousemove & mouseup event handlers if drag-creation is allowed but editableEvents.resize is set to false.
Version 3.5.1
  • Don't fire cell-click when clicking on an event
  • Focus and highlight cell on mousedown rather than click
Version 3.5.0 Create events with click & drag. Refer to the Create events example.
Version 3.4.1 Also return the original event from the emitted event-duration-change event
Version 3.4.0 Add a split-label slot for day splits labels. Refer to the Custom day split labels example.
Version 3.3.1 Add custom event renderer back in all-day bar events
Version 3.3.0 Added the allDayBarHeight option and fix the all-day bar layout when using minCellWidth or day splits & minSplitWidth
Version 3.2.8 Call onEventClick() (if any) on event click if events are not editable or on event creation
Version 3.2.7 Don't call onEventClick() (if any) on event click & hold
Version 3.2.6 Display the no-event slot accurately per slot if there are
Version 3.2.5 Fix emitting view-change between week & day views
Version 3.2.4 Prevent calling onEventClick after event resize & focus event on resize
Version 3.2The new two way binding active-view prop replaces the default-view prop.
Refer to the external controls example.
Version 3.1.1 Allow disabling event titleEditable individually
Version 3.1.0

The event properties startDate and endDate have been merged into start and end which now accept both a String and a Javascript Date.
Vue Cal always returns the Date object and not the string, even if you defined it as a string, but Vue Cal offers Date prototype functions to easily format the date how you want.

  • Fixed multiple day events resizing on x and y axis.
  • Allow disabling Date prototypes
  • Emit an event-resizing repeatedly while resizing an event
Version 3.0

The arrival of the drag & drop feature marks a new milestone for Vue Cal!

Many subsequent features to come, progressively building the most intuitive full-featured and flexible calendar on Vue.js, 100% designed for Vue, and still no dependency!

Like the native HTML5 drag & drop it's built with, Vue Cal's drag & drop is not available on touch screens

Vue Cal will support touch screen drag & drop later on, using an alternative technology.

New Features

  • Events drag & drop

    Drag & drop is a module (to keep Vue Cal light weight) and must be loaded separately:
    import 'vue-cal/dist/drag-and-drop.js'

  • Drop an external (HTML5 draggable) event into Vue Cal or between 2 Vue Cal instances

  • snapToTime option on event drop and event resize

    Refer to the snapToTime option in the API section.

  • The editableEvents option now also accept an object to precisely allow specific edition

    Refer to the editableEvents option in the API section.

Big changes

  • In the coming version 3.1:

    Now that Vue Cal has Date prototypes and it is so easy to format a date, the event properties startDate and endDate will be removed and the start and end of event will be exlusively defined through start and end. It will accept both a String and a Javascript Date.
    Vue Cal will always return the Date object and not the string, even if you defined it as a string.

  • The event-change emitted event now returns an object containing the event and the originalEvent.
  • the event-title-change and event-duration-change events now return an object containing the event and the oldTitle or oldDate.
  • Vue Cal's createEvent() function now accepts a duration parameter to easily override the default 2 hours. (ref. Create events example)
  • The internal event classes property is replaced with class like in the external event definition. This means you can now update the class property seemlessly like the initial event definition. (From your component methods called from Vue Cal fired events or from onEventCreate)
  • Renamed slot

    The event-renderer slot is renamed into event

  • Huge code refactoring

    Introducing Vue dependency injections, Utils classes, and a couple of improvements on event resize.

Other noticeable changes

  • When creating an event with a given endDate, the required endTimeMinutes is automatically add.
  • You don't need to call alignAllDayBar() anymore

    Previously, in some cases you would need to call this function to realign the all-day bar with the scrollbar when the scrollbar is fixed (E.g. on Windows).
    Now the function (renamed to alignWithScrollbar) is triggered automatically and in all the cases, once, in mounted.
    It will now also align the weekdays headings if needed.

  • Renamed CSS classes

    If you use them in your own CSS (or if you have a custom color theme) you might want to update them:

    Cells
    • .current becomes .vuecal__cell--current
    • .today becomes .vuecal__cell--today
    • .out-of-scope becomes .vuecal__cell--out-of-scope
    • .before-min becomes .vuecal__cell--before-min
    • .after-max becomes .vuecal__cell--after-max
    • .disabled becomes .vuecal__cell--disabled
    • .selected becomes .vuecal__cell--selected
    • .vuecal__cell--has-splits & .vuecal__cell--has-events remain the same
    View selector buttons
    • Added class: .vuecal__view-btn
    • .active becomes .vuecal__view-btn--active
  • New CSS classes when an event is dragged

    • Over a cell: .vuecal__cell--highlighted
    • Over a menu arrow (previous & next): .vuecal__arrow--highlighted
    • Over a menu view button: .vuecal__view-btn--highlighted
    • Event dragging class: .vuecal__event-dragging
  • Updated color theme

    If you have a custom color theme, these new classes should be added: .vuecal__view-btn--highlighted, .vuecal__arrow--highlighted, .vuecal__cell--highlighted.
    Refer to the CSS Notes.