The Vue monthly calendar arranges dates into week rows, with one cell per day. Events can occupy one day or span several dates; an event crossing a week boundary continues on the next row. The first and last rows include dates from neighboring months.

The component is available in DayPilot Lite (open-source, Apache License 2.0) and DayPilot Pro (commercial). Both include Vue integration and TypeScript declarations.

Vue Monthly Calendar Component (DayPilot)

Vue Monthly Calendar Features

DayPilot Lite and Pro

  • One-day and multi-day events. Each record supplies an ID, text, start, and end. Event ends are exclusive: the conference in the introductory example starts on October 5 and ends on October 8, so it occupies October 5–7. Month places the event in the appropriate cells and week rows.

  • Select, move, and resize. Select days to create an event, move it to another date, or resize its duration. The selection callback supplies dates to your editor; callbacks after moving or resizing can save the updated record.

  • Colors and event actions. Set event colors, customize day cells, and attach a context menu. Active areas add positioned icons or controls inside an event. The first-day-of-week setting and locale control the calendar's date layout and formatting.

  • Navigation and connected views. Change the displayed month using startDate, or connect a Vue date picker. Share event data with the day/week Calendar. When loading events, use the full visible range, including adjacent-month days; the loading example below shows how to read it.

Additional DayPilot Pro Features

  • Image export. Create a JPEG, PNG, or SVG image of the calendar. The export guide explains the supported event styling and how to obtain the output.

  • Excel export. Export the full month grid to an Excel spreadsheet, including overlapping and multi-day events. Your application supplies an ExcelJS reference; the component maps the calendar dates and events into spreadsheet cells.

See the feature matrix for a detailed edition comparison.

Add Actions That Use the Selected Event

The Vue monthly-calendar example adds a context menu with Edit, Delete, Postpone, Duplicate, Lock, and Type actions. Postpone shifts both dates by one day, preserving the duration. Duplicate copies the record and assigns a new ID. The sample's type field selects the event color. Try the event menu (Lite).

Locking demonstrates how several interactions can follow one application field. The sample reads locked when rendering an event, adds a lock icon, and disables moving, resizing, clicking, and deletion. Before opening the menu, it disables the corresponding items and changes Lock to Unlock. Month supplies the event-level interaction settings and menu callbacks; the sample defines what its locked state means.

Reuse a Vue Event Component Across Views

The combined-view example puts a custom CalendarEvent component in the #event slots of Calendar and Month. It shows the event name and edit/delete icons, with a compact style for the month grid. The parent handles the actions and updates the shared events array, so all three views receive the changed records. Try the shared event controls (Lite).

A slot receives the current DayPilot.Event, allowing the content to use the event's fields and call your Vue handlers. The basic template example below starts with bold event text; the linked project shows how the same mechanism supports reusable components and controls.

Demos and Tutorials

Each tutorial includes a downloadable project. The edition used by the example is shown beside its link.

Monthly and Combined Views

Configure a Starter Project

Use Month UI Builder to explore settings in a live preview and download a Vue starter. Select the edition that matches your application. Continue adjusting the generated configuration in your own code.

Installation

DayPilot Lite

Install @daypilot/daypilot-lite-vue from npm:

npm install @daypilot/daypilot-lite-vue

DayPilot Pro

At npm.daypilot.org, select Vue and copy the installation command for daypilot-pro-vue. Licensed customers can obtain their package URL in the customer area.

The examples use Vue 3 and the Composition API with <script setup>.

Display a Monthly Calendar

This calendar shows a three-day conference in October.

Month.vue

<script setup>
import {ref} from 'vue';
import {DayPilot, DayPilotMonth} from '@daypilot/daypilot-lite-vue';

const events = ref([
  {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
]);
</script>

<template>
  <DayPilotMonth
    startDate="2026-10-01"
    :weekStarts="1"
    :events="events"
  />
</template>

startDate selects the month; weekStarts: 1 starts each row on Monday. The conference occupies October 5–7: its October 8 end is exclusive. The grid also displays dates from adjacent months to fill its first and last weeks.

The examples use Lite. For Pro, change the package import to daypilot-pro-vue.

See event loading for data formats and loading options.

Load the Full Visible Month

Navigation changes the month before reading visibleStart() and visibleEnd(). These methods include the adjacent-month days in the grid. Request events that overlap this entire range, rather than only events inside the named month.

MonthLoading.vue

<script setup>
import {ref, watch, nextTick} from 'vue';
import {DayPilot, DayPilotMonth} from '@daypilot/daypilot-lite-vue';

const startDate = ref(new DayPilot.Date('2026-10-01'));

const month = ref(null);

const events = ref([]);

watch([month, startDate], async (_, __, onCleanup) => {
  if (!month.value) { return; }
  let active = true;
  onCleanup(() => { active = false; });
  await nextTick();
  if (!active) { return; }
  const start = month.value.control.visibleStart();
  const end = month.value.control.visibleEnd();
  const url = `/api/events?start=${start}&end=${end}`;
  try {
    const {data} = await DayPilot.Http.get(url);
    if (!active) { return; }
    events.value = data;
  } catch {
    if (active) { await DayPilot.Modal.alert('Could not load data.'); }
  }
}, {immediate: true, flush: 'post'});

function navigate(offset) {
  startDate.value = startDate.value.addMonths(offset);
}
</script>

<template>
  <button @click="navigate(-1)">Previous month</button>
  <button @click="navigate(1)">Next month</button>
  <DayPilotMonth
    :startDate="startDate"
    :weekStarts="1"
    :events="events"
    ref="month"
  />
</template>

The /api/events URL represents your own backend. Return items that overlap the requested range: item start is before the requested end, and item end is after the requested start. Failed requests display a DayPilot dialog.

The watcher cleanup prevents an older response from replacing data after another navigation action.

The watcher waits for nextTick() before reading the range, allowing the wrapper to apply its queued date update.

Create Events by Selecting Time

This complete variation of the first example handles time range selection. The selection supplies the dates; the dialog supplies the name.

MonthCreation.vue

<script setup>
import {ref} from 'vue';
import {DayPilot, DayPilotMonth} from '@daypilot/daypilot-lite-vue';

const events = ref([
  {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
]);

async function onTimeRangeSelected(args) {
  const modal = await DayPilot.Modal.prompt('Event name:', 'New event');
  args.control.clearSelection();
  if (modal.canceled) { return; }
  args.control.events.add({
    id: DayPilot.guid(), text: modal.result,
    start: args.start, end: args.end
  });
}
</script>

<template>
  <DayPilotMonth
    startDate="2026-10-01"
    :weekStarts="1"
    :events="events"
    @timeRangeSelected="onTimeRangeSelected"
  />
</template>

Canceling the dialog leaves the data unchanged. events.add() inserts the new event locally. In an application with a backend, create the record with a POST request and use the returned ID before adding it to the view.

Move, Resize and Save Events

Moving changes the event position; resizing changes its duration. Both work in the first example without extra configuration. The component updates the display and the supplied data object automatically.

Use the callbacks after the edit to persist the result. This example sends the changed record to the backend; it does not reload the collection after each drag.

MonthSaving.vue

<script setup>
import {ref} from 'vue';
import {DayPilot, DayPilotMonth} from '@daypilot/daypilot-lite-vue';

const events = ref([
  {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
]);

async function saveEvent(data) {
  try {
    await DayPilot.Http.put(`/api/events/${data.id}`, data);
  } catch {
    await DayPilot.Modal.alert('The change could not be saved.');
  }
}
</script>

<template>
  <DayPilotMonth
    startDate="2026-10-01"
    :weekStarts="1"
    :events="events"
    @eventMoved="saveEvent($event.e.data)"
    @eventResized="saveEvent($event.e.data)"
  />
</template>

The /api/events/{id} endpoint accepts the updated JSON record. A failed save is reported while the local edit remains visible; add retry or rollback behavior to suit your application. To disable an interaction, set eventMoveHandling or eventResizeHandling to "Disabled".

Handle an Event Click

To display the clicked item’s name, add this listener to the existing component tag:

@eventClick="DayPilot.Modal.alert(DayPilot.Util.escapeHtml($event.e.text()))"

DayPilot.Modal.alert() accepts HTML. escapeHtml() displays the item’s name as plain text.

Render Custom Vue Content

This independent variation of the first example adds a scoped slot for the event content.

MonthContent.vue

<script setup>
import {ref} from 'vue';
import {DayPilot, DayPilotMonth} from '@daypilot/daypilot-lite-vue';

const events = ref([
  {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
]);
</script>

<template>
  <DayPilotMonth
    startDate="2026-10-01"
    :weekStarts="1"
    :events="events"
  >
    <template #event="{event}">
      <strong>{{ event.text() }}</strong>
    </template>
  </DayPilotMonth>
</template>

Month supports event and cell templates. The cell slot supplies dates and cell.events() for the events in that day.

In Pro, templates also track reactive parent data: changing a value used inside a template refreshes its content automatically. The wrapper handles mounting and cleanup of template content.

Access the Month API

Use the event collection API to find and rename the conference. The wrapper reference, button and handler are shown together.

MonthApi.vue

<script setup>
import {ref} from 'vue';
import {DayPilot, DayPilotMonth} from '@daypilot/daypilot-lite-vue';

const month = ref(null);

const events = ref([
  {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
]);

async function renameEvent() {
  const control = month.value.control;
  const event = control.events.find('conference');
  const modal = await DayPilot.Modal.prompt('Event name:', event.text());
  if (modal.canceled) { return; }
  event.text(modal.result);
  control.events.update(event);
}
</script>

<template>
  <button @click="renameEvent">Rename conference</button>
  <DayPilotMonth
    startDate="2026-10-01"
    :weekStarts="1"
    :events="events"
    ref="month"
  />
</template>

The Vue template ref exposes the underlying control as month.value.control. The wrapper creates and disposes it. See the DayPilot.Month API for methods, properties and callbacks.

Availability

Availability of this feature in DayPilot editions:

ProductLitePro
DayPilot for JavaScript
DayPilot for ASP.NET WebForms
DayPilot for ASP.NET MVC
DayPilot for Java