The Angular monthly calendar displays events in a familiar month grid. It works well for leave planning, reservations, reminders, and milestones, including events that span several days.

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

angular monthly event calendar component

Angular Monthly Calendar Features

  • A complete month at a glance. Show daily cells with appointments, reminders, and events spanning multiple days. Events that cross a week boundary continue on the following row.

  • Interactive scheduling. Let users select days to create an event, move events to another date, and resize their duration. Your application chooses the editor and saves the result.

  • Custom appearance. Set event colors, customize cell appearance, and use a context menu for application actions. Locales and the first-day-of-week setting adapt the display to your users.

  • Connected views. Share event data with the day/week Calendar and use the Navigator date picker to change the displayed month.

Additional Pro Options

Pro adds advanced scheduling and export options, including image export and Excel export. Choose the edition according to the required interactions and output formats; the core monthly view and ordinary event editing are available in both.

See the feature matrix for a detailed edition comparison.

Demos and Tutorials

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

Calendar Views

Older Angular Projects

Configure a Starter Project

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

Installation

DayPilot Lite

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

npm install @daypilot/daypilot-lite-angular

DayPilot Pro

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

The examples use standalone Angular components. Import DayPilotModule in the component that contains the DayPilot tag.

Display a Monthly Calendar

This calendar shows a three-day conference in October.

month.component.ts

import {Component} from '@angular/core';
import {DayPilot, DayPilotModule} from '@daypilot/daypilot-lite-angular';

@Component({
  selector: 'app-month',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <daypilot-month
      [config]="config"
      [events]="events"></daypilot-month>
  `
})
export class MonthComponent {
  config: DayPilot.MonthConfig = {
    startDate: '2026-10-01',
    weekStarts: 1
  };

  events: DayPilot.EventData[] = [
    {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
  ];
}

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.

Import MonthComponent in the parent component and use <app-month></app-month> in its template.

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

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.

month-loading.component.ts

import {
  Component, ChangeDetectorRef, inject, AfterViewInit, ViewChild
} from '@angular/core';
import {
  DayPilot, DayPilotModule, DayPilotMonthComponent
} from '@daypilot/daypilot-lite-angular';

@Component({
  selector: 'app-month',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <button (click)="navigate(-1)">Previous month</button>
    <button (click)="navigate(1)">Next month</button>
    <daypilot-month
      #month
      [config]="config"
      [events]="events"></daypilot-month>
  `
})
export class MonthComponent implements AfterViewInit {
  @ViewChild('month') month!: DayPilotMonthComponent;

  config: DayPilot.MonthConfig = {
    startDate: '2026-10-01',
    weekStarts: 1
  };

  events: DayPilot.EventData[] = [];

  private changes = inject(ChangeDetectorRef);

  private loadRequest = 0;

  ngAfterViewInit() {
    this.loadData();
  }

  navigate(offset: number) {
    const start = new DayPilot.Date(this.config.startDate!).addMonths(offset);
    this.config = {...this.config, startDate: start};
    this.changes.detectChanges();
    this.loadData();
  }

  async loadData() {
    const request = ++this.loadRequest;
    const start = this.month.control.visibleStart();
    const end = this.month.control.visibleEnd();
    const url = `/api/events?start=${start}&end=${end}`;
    try {
      const {data} = await DayPilot.Http.get(url);
      if (request !== this.loadRequest) { return; }
      this.events = data;
      this.changes.markForCheck();
    } catch {
      if (request === this.loadRequest) {
        await DayPilot.Modal.alert('Could not load data.');
      }
    }
  }
}

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 request counter prevents an older response from replacing data after another navigation action.

detectChanges() applies the new configuration before the range is read. After the asynchronous response, markForCheck() lets Angular display the new event array, including in zoneless applications.

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.

month-creation.component.ts

import {Component} from '@angular/core';
import {DayPilot, DayPilotModule} from '@daypilot/daypilot-lite-angular';

@Component({
  selector: 'app-month',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <daypilot-month
      [config]="config"
      [events]="events"></daypilot-month>
  `
})
export class MonthComponent {
  config: DayPilot.MonthConfig = {
    startDate: '2026-10-01',
    weekStarts: 1,
    onTimeRangeSelected: async 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
      });
    }
  };

  events: DayPilot.EventData[] = [
    {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
  ];
}

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.

month-saving.component.ts

import {Component} from '@angular/core';
import {DayPilot, DayPilotModule} from '@daypilot/daypilot-lite-angular';

@Component({
  selector: 'app-month',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <daypilot-month
      [config]="config"
      [events]="events"></daypilot-month>
  `
})
export class MonthComponent {
  config: DayPilot.MonthConfig = {
    startDate: '2026-10-01',
    weekStarts: 1,
    onEventMoved: args => this.saveEvent(args.e.data),
    onEventResized: args => this.saveEvent(args.e.data)
  };

  events: DayPilot.EventData[] = [
    {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
  ];

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

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 a Event Click

To display the clicked item’s name, add this callback to config:

onEventClick: args => DayPilot.Modal.alert(DayPilot.Util.escapeHtml(args.e.text()))

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

Access the Month API

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

month-api.component.ts

import {Component, ViewChild} from '@angular/core';
import {
  DayPilot, DayPilotModule, DayPilotMonthComponent
} from '@daypilot/daypilot-lite-angular';

@Component({
  selector: 'app-month',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <button (click)="renameEvent()">Rename conference</button>
    <daypilot-month
      #month
      [config]="config"
      [events]="events"></daypilot-month>
  `
})
export class MonthComponent {
  @ViewChild('month') month!: DayPilotMonthComponent;

  config: DayPilot.MonthConfig = {
    startDate: '2026-10-01',
    weekStarts: 1
  };

  events: DayPilot.EventData[] = [
    {id: 'conference', text: 'Conference', start: '2026-10-05', end: '2026-10-08'}
  ];

  async renameEvent() {
    const control = this.month.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);
  }
}

@ViewChild provides the wrapper; its control property exposes the underlying JavaScript component after initialization. The wrapper creates and disposes it. See the DayPilot.Month API for methods, properties and callbacks.

Load Data into Signal Inputs

If your application stores the collection in an Angular signal, pass that signal to the same input. This standalone variation loads data and calls set(); the wrapper observes the new collection.

month-signal-loading.component.ts

import {
  Component, ChangeDetectorRef, inject, AfterViewInit, ViewChild, signal
} from '@angular/core';
import {
  DayPilot, DayPilotModule, DayPilotMonthComponent
} from '@daypilot/daypilot-lite-angular';

@Component({
  selector: 'app-month',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <button (click)="navigate(-1)">Previous month</button>
    <button (click)="navigate(1)">Next month</button>
    <daypilot-month
      #month
      [config]="config"
      [events]="events"></daypilot-month>
  `
})
export class MonthComponent implements AfterViewInit {
  @ViewChild('month') month!: DayPilotMonthComponent;

  config: DayPilot.MonthConfig = {
    startDate: '2026-10-01',
    weekStarts: 1
  };

  events = signal<DayPilot.EventData[]>([]);

  private changes = inject(ChangeDetectorRef);

  private loadRequest = 0;

  ngAfterViewInit() {
    this.loadData();
  }

  navigate(offset: number) {
    const start = new DayPilot.Date(this.config.startDate!).addMonths(offset);
    this.config = {...this.config, startDate: start};
    this.changes.detectChanges();
    this.loadData();
  }

  async loadData() {
    const request = ++this.loadRequest;
    const start = this.month.control.visibleStart();
    const end = this.month.control.visibleEnd();
    const url = `/api/events?start=${start}&end=${end}`;
    try {
      const {data} = await DayPilot.Http.get(url);
      if (request !== this.loadRequest) { return; }
      this.events.set(data);
    } catch {
      if (request === this.loadRequest) {
        await DayPilot.Modal.alert('Could not load data.');
      }
    }
  }
}

Bind the signal itself with [events]="events". Binding its current value with () instead uses the ordinary input path. Keep one form consistently. Signals require Angular 16 or later.

Signal configuration/event inputs are supported since DayPilot Pro 2025.1.6377 and DayPilot Lite Angular 5.0.0.

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