How to Create a Calendar With Vuetify

In this article, we’ll learn about the Vuetify calendar component and its various props and slots for creating and customizing calendars that can display events and other time-related information in various ways.

The Vuetify calendar component (v-calendar)

Vuetify provides the v-calendar component for displaying a calendar.

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar></v-calendar> </div> </v-app> </template> <script> export default { name: 'App', }; </script> 
Displaying a calendar with the Vuetify calendar component.

Vuetify calendar events

We can display events in the calendar with the events prop. This property takes an array with each element representing one event. Each item in the array is an object that has a number of properties, including:

  • name: sets the name of the event.
  • start: sets the start date of the event.
  • end: sets the end date of the event.
  • timed: specifies whether the event has a defined time range or not.
App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :events="events" now="2022-04-17" ></v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => { return { events: [ { name: 'Event 1', start: '2022-04-01', timed: false, }, { name: 'Event 2', start: '2022-04-05', end: '2022-04-07', }, { name: 'Event 3', start: '2022-04-09T08:00:00', end: '2022-04-09T10:00:00', timed: true, }, ], }; }, }; </script> 
Using the events prop of the Vuetify calendar component.

Vuetify calendar colors

The v-calendar component provides various ways of customizing the colors of its different parts. For example, we can set the color of a single event by passing a value to the color property of the object representing the event:

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :events="events" now="2022-04-17" ></v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => { return { events: [ { name: 'Event 1', start: '2022-04-01', timed: false, color: 'green', }, { name: 'Event 2', start: '2022-04-05', end: '2022-04-07', color: 'yellow darken-3', }, { name: 'Event 3', start: '2022-04-09T08:00:00', end: '2022-04-09T10:00:00', timed: true, color: 'red', }, ], }; }, }; </script> 
Customizing event colors on the Vuetify calendar component.

event-color and color props

We can also the event-color prop to set the color of all events without a specified color. The color prop of v-calendar sets the color of the circle that indicates the current date in the calendar, and the color of the current day of the week.

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :events="events" now="2022-04-17" event-color="red accent-2" color="red accent-2" > </v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => { return { events: [ { name: 'Event 1', start: '2022-04-01', timed: false, }, { name: 'Event 2', start: '2022-04-05', end: '2022-04-07', }, { name: 'Event 3', start: '2022-04-09T08:00:00', end: '2022-04-09T10:00:00', timed: true, }, ], }; }, }; </script> 
Using the event-color and color props of the Vuetify calendar component.

Vuetify calendar day view

By default, the calendar component displays information for each month. We can customize the view the calendar shows with the type prop. We can display information just for a particular day by setting type to day:

JavaScript
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :events="events" now="2022-04-17" value="2022-04-20" type="day" ></v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => { return { events: [ { name: 'Event 1', start: '2022-04-20T02:00:00', end: '2022-04-20T04:00:00', timed: true, }, { name: 'Event 2', start: '2022-04-20T05:00:00', end: '2022-04-20T06:00:00', timed: true, }, { name: 'Event 3', start: '2022-04-20T09:00:00', end: '2022-04-20T10:00:00', timed: true, }, ], }; }, }; </script> 
Displaying the day view of the Vuetify calendar component.

Calendar day view slots

day-header and interval

There are various slots of the v-calendar component that we use to customize how the calendar displays in the day view. We can use the day-header slot to customize the content that is placed in the top container in the day view. We also have the interval slot, for customizing the content that is placed in the interval space in the day view.

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :events="events" now="2022-04-17" value="2022-04-20" type="day" > <template v-slot:day-header="{ present }"> <div v-if="present" class="text-center" > Today </div> </template> <template v-slot:interval="{ hour }"> <div class="text-center">{{ hour }} o'clock</div> </template> </v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => { return { events: [ { name: 'Event 1', start: '2022-04-20T02:00:00', end: '2022-04-20T04:00:00', timed: true, }, { name: 'Event 2', start: '2022-04-20T05:00:00', end: '2022-04-20T06:00:00', timed: true, }, { name: 'Event 3', start: '2022-04-20T09:00:00', end: '2022-04-20T10:00:00', timed: true, }, ], }; }, }; </script> 
Using the day-header and the interval slots of the Vuetify calendar component.

day-body

The day-body slot allows us to customize the content in the scrollable interval container in the day view of the calendar.

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar ref="calendar" type="week" value="2022-04-13" > <template v-slot:day-body="{ date, week }"> <div class="current-time" :class="{ first: date === week[0].date }" :style="{ top: nowY }" ></div> </template> </v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => ({ ready: false, }), computed: { cal() { return this.ready ? this.$refs.calendar : null; }, nowY() { return this.cal ? this.cal.timeToY({ hour: 8, minute: 10 }) + 'px' : '-10px'; }, }, mounted() { this.ready = true; this.scrollToTime(); this.updateTime(); }, methods: { getCurrentTime() { return this.cal ? this.cal.times.now.hour * 60 + this.cal.times.now.minute : 0; }, scrollToTime() { const time = this.getCurrentTime(); const first = Math.max(0, time - (time % 30) - 30); this.cal.scrollToTime(first); }, updateTime() { setInterval(() => this.cal.updateTimes(), 60 * 1000); }, }, }; </script> <style lang="scss"> .current-time { height: 2px; background-color: red; position: absolute; left: -1px; right: 0; pointer-events: none; &.first::before { content: ''; position: absolute; background-color: red; width: 12px; height: 12px; border-radius: 50%; margin-top: -5px; margin-left: -6.5px; } } </style> 
Using the day-body slot of the Vuetify calendar component.

Calendar week view

We can set the type prop to week to make the calendar display information only for a particular week:

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :events="events" type="week" event-color="indigo" value="2022-04-16" now="2022-04-16" > </v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => { return { events: [ { name: 'Event 1', start: '2022-04-10T02:00:00', end: '2022-04-10T04:00:00', timed: true, }, { name: 'Event 2', start: '2022-04-12T08:00:00', end: '2022-04-12T10:00:00', timed: true, }, { name: 'Event 3', start: '2022-04-15T04:00:00', end: '2022-04-15T06:00:00', timed: true, }, ], }; }, }; </script> 
Displaying the week view of the Vuetify calendar component.

Vuetify calendar click events

v-calendar comes with certain click events that we can use to add interactivity to the calendar. For example, we can listen for the @click:day event to perform an action when the user clicks a day in the calendar. Similarly, we can listen for the @click:event event to perform an action when the user clicks on an event in the calendar. We use both click events in the code example below.

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <!-- Event details menu --> <v-menu v-model="selectedOpen" :close-on-content-click="false" :activator="selectedElement" offset-x > <v-card min-width="200px"> <v-toolbar color="purple accent-4" dark > <v-toolbar-title> {{ selectedEvent.name }} </v-toolbar-title> </v-toolbar> <v-card-text> <div class="text-center"> {{ selectedEventStart }} to {{ selectedEventEnd }} </div> <v-checkbox readonly label="All day" :value="!selectedEvent.timed" > </v-checkbox> </v-card-text> </v-card> </v-menu> <v-calendar :events="events" event-color="purple accent-4" @click:day="viewDay" @click:event="showEvent" v-model="focus" :type="type" now="2022-04-23" ></v-calendar> </div> </v-app> </template> <script> import { format } from 'date-fns'; export default { name: 'App', data: () => { return { focus: '', type: 'month', selectedOpen: false, selectedElement: undefined, selectedEvent: {}, events: [ { name: 'Event 1', start: '2022-04-01', timed: false, }, { name: 'Event 2', start: '2022-04-04', end: '2022-04-06', }, { name: 'Event 3', start: '2022-04-04T05:00:00', end: '2022-04-04T07:00:00', timed: true, }, { name: 'Event 4', start: '2022-04-08T08:00:00', end: '2022-04-08T10:00:00', timed: true, }, ], }; }, methods: { viewDay({ date }) { this.focus = date; this.type = 'day'; }, showEvent({ nativeEvent, event }) { const open = () => { this.selectedEvent = event; this.selectedElement = nativeEvent.target; requestAnimationFrame(() => requestAnimationFrame( () => (this.selectedOpen = true) ) ); }; if (this.selectedOpen) { this.selectedOpen = false; requestAnimationFrame(() => requestAnimationFrame(() => open()) ); } else { open(); } nativeEvent.stopPropagation(); }, }, computed: { selectedEventStart() { return ( (this.selectedEvent && this.selectedEvent.start && format( new Date(this.selectedEvent.start), 'h:mm a' )) || '12:00 AM' ); }, selectedEventEnd() { return ( (this.selectedEvent && this.selectedEvent.end && format( new Date(this.selectedEvent.end), 'h:mm a' )) || '12:00 AM' ); }, }, }; </script> 

The calendar starts out in the month view. When the user clicks a day, we change the calendar to the day view:

Using the @click:day event to add interactivity to the Vuetify calendar component.

We also created a menu that will display information about an event when the user clicks it in the day or month view:

Using the @click:event event to add interactivity to the Vuetify calendar component.

Custom calendar buttons

Calendar day slot

We can use the day slot of v-calendar to customize how a day is displayed in the week or month view. In the example below, we use this slot to create a calendar that displays the percentage of a day that was used for activities of a certain category.

App.vue
<template> <v-app> <div class="ma-4" style="height: 100%" > <v-calendar :now="today" :value="today" color="primary" > <template v-slot:day="{ past, date }"> <v-row class="fill-height"> <template v-if="past && tracked[date]"> <v-sheet v-for="(percent, i) in tracked[date]" :key="i" :title="category[i]" :color="colors[i]" :width="`${percent}%`" height="100%" tile ></v-sheet> </template> </v-row> </template> </v-calendar> </div> </v-app> </template> <script> export default { name: 'App', data: () => ({ today: '2022-04-11', tracked: { '2022-04-01': [20, 45, 10], '2022-04-02': [40, 30, 0], '2022-04-03': [10, 25, 50], '2022-04-04': [25, 25, 20], '2022-04-05': [20], '2022-04-06': [20, 60, 10], '2022-04-07': [0, 0, 25], '2022-04-08': [60, 15, 20], '2022-04-09': [45, 0, 5], '2022-04-10': [30, 20, 20], }, colors: ['blue', 'yellow darken-3', 'red accent-2'], category: ['Category 1', 'Category 2', 'Category 3'], }), }; </script> 
Using the day slot of the Vuetify calendar component.

Conclusion

We can use the Vuetify calendar component (v-calendar) to create a calendar that displays events and other information for our application. This component comes with various props and slots that we can use to easily customize its appearance and behavior.



Every Crazy Thing JavaScript Does

A captivating guide to the subtle caveats and lesser-known parts of JavaScript.

Every Crazy Thing JavaScript Does

Sign up and receive a free copy immediately.


Leave a Comment

Your email address will not be published. Required fields are marked *