DayPilot Angular Calendar is an interactive calendar component for appointment booking, room and equipment reservations, staff schedules, and timetables. Time runs vertically, with days or resources displayed as columns. You can start with a day or week view and add date navigation, drag-and-drop editing, and the event details your application needs.

The component is available in DayPilot Lite, the open-source edition under the Apache License 2.0, and DayPilot Pro, the commercial edition with additional scheduling features. Both provide Angular components, TypeScript definitions, and bindings for configuration and event data.

Try the Angular calendar demo · Get the starter project · Configure a calendar in UI Builder

Angular Event Calendar Component (DayPilot)

Angular Calendar Features

The day and week layouts work well when users need to see the timing and duration of appointments. A resource calendar uses the same vertical time axis to compare bookings for several people, rooms, or machines side by side.

DayPilot Lite and Pro

  • Day, work-week, week, and custom day views. Choose the calendar view and number of days to display. Combine Calendar with the Angular monthly calendar when your application needs a month overview as well.

  • Resources as columns. Display a daily schedule for staff, rooms, or equipment using the resource calendar. Each event belongs to a resource column, and users can move it to a different resource.

  • Interactive event editing. Let users select a time range to create an event, then move or resize it using drag and drop. Handle these actions in Angular callbacks to open an editor, update your application state, and save changes to your backend.

  • Event appearance and CSS themes. Use colors, labels, and event actions to make different types of appointments easy to recognize. Event customization, cell customization, and CSS themes let you adapt the calendar to your application.

  • Working hours and date formatting. Set business hours, time-slot duration, and the first day of the week. Use a locale to format dates and times for your users.

  • Date navigation and Angular state. Connect an Angular date picker, add previous/next buttons, and load events for the selected period. Bind options and event data using signals or ordinary objects and arrays.

Additional DayPilot Pro Features

The Pro demos above show the underlying JavaScript Calendar features that you can configure through the Angular component. For the complete edition comparison, see the feature matrix. If you need time to run horizontally with resources as rows, see the Angular Scheduler.

Angular Calendar Demos and Tutorials

Explore the live examples before adding the component to your application. Each tutorial explains the implementation and provides a downloadable Angular project. The edition is shown for each example so you can start with the package you plan to use.

Calendar Views and Date Navigation

Editing, Appearance, and Layout

Backend Integration and Specialized Schedules

  • Angular Appointment Calendar with PHP/MySQL (Lite)
    A complete example of loading appointments and saving changes through a REST API. Its Angular scaffolding predates the standalone setup below; use it for the backend workflow and adapt the project setup to your application.

  • Angular Timetable (Pro)
    Build a weekly timetable with custom time blocks and labels. The tutorial uses an older Angular project structure but illustrates a specialized Calendar layout.

  • Angular Calendar: Displaying a Custom Set of Days (Pro)
    Display a selected set of dates as columns, including non-consecutive dates. This example uses an older Angular project structure; the column configuration is the main pattern to reuse.

Configure an Angular Calendar Visually

calendar ui builder configurator

Calendar UI Builder lets you change the calendar's appearance and behavior while seeing the result immediately. Select an Angular target and the edition you want to use, then download a starter project with your configuration. You can continue editing the options in TypeScript after downloading the project.

Installation and Editions

Install the package for the edition you use. The integration examples below use DayPilot Lite; for DayPilot Pro, change the package import as shown below.

DayPilot Lite (Open-Source)

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

npm install @daypilot/daypilot-lite-angular

DayPilot Pro

The Angular package is included in DayPilot Pro for JavaScript. At npm.daypilot.org, select Angular and copy the release installation command for daypilot-pro-angular. Licensed customers can get their package installation URL in the customer area.

The signal-based examples below require Angular 16 or higher. Calendar signal inputs are supported since DayPilot Pro 2025.1.6377 and DayPilot Lite Angular 5.0.0. Use Node.js and TypeScript versions compatible with your Angular release.

Display a Calendar in a Standalone Component

Import DayPilotModule in the host component's imports array. It provides the <daypilot-calendar> component. Use DayPilot.CalendarConfig for the options and DayPilot.EventData[] for the event collection.

This complete component displays a week containing the sample event, provides previous/next navigation, and keeps the Angular event collection in sync after dragging or resizing an event.

calendar.component.ts

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

@Component({
  selector: 'app-calendar',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <button (click)="changeWeek(-7)">Previous week</button>
    <button (click)="changeWeek(7)">Next week</button>
    <button (click)="showRange()">Show visible range</button>
    <p>{{ visibleRange() }}</p>
    <daypilot-calendar
      #calendar
      [config]="config"
      [events]="events">
    </daypilot-calendar>
  `
})
export class CalendarComponent implements AfterViewInit {
  @ViewChild('calendar') calendar!: DayPilotCalendarComponent;

  visibleRange = signal('');

  config = signal<DayPilot.CalendarConfig>({
    viewType: 'Week',
    weekStarts: 1,
    startDate: '2026-09-28',
    eventMoveHandling: 'Update',
    eventResizeHandling: 'Update',
    onEventMoved: args => this.eventChanged(args.e),
    onEventResized: args => this.eventChanged(args.e)
  });

  events = signal<DayPilot.EventData[]>([
    {
      id: 1,
      start: '2026-09-29T10:00:00',
      end: '2026-09-29T12:00:00',
      text: 'Planning meeting'
    }
  ]);

  ngAfterViewInit(): void {
    const calendar = this.calendar.control;
    console.log('Visible range', calendar.visibleStart(), calendar.visibleEnd());
  }

  changeWeek(days: number): void {
    this.config.update(config => ({
      ...config,
      startDate: new DayPilot.Date(config.startDate!).addDays(days)
    }));
  }

  eventChanged(event: DayPilot.Event): void {
    this.events.update(events => events.map(item => item.id === event.id()
      ? {...item, start: event.start(), end: event.end()}
      : item
    ));
    // Save the new dates to your backend here.
    console.log('Event changed', event.id(), event.start(), event.end());
  }

  showRange(): void {
    const calendar = this.calendar.control;
    this.visibleRange.set(`${calendar.visibleStart()} – ${calendar.visibleEnd()}`);
  }
}

Import CalendarComponent in your standalone parent component's imports array and add <app-calendar></app-calendar> to its template. To use this component as the root of a minimal browser application, bootstrap it with bootstrapApplication(CalendarComponent) and use <app-calendar> as the root element in index.html.

For DayPilot Pro, replace the DayPilot import in each example with the corresponding import from daypilot-pro-angular. For the complete example above:

import {DayPilot, DayPilotCalendarComponent, DayPilotModule} from 'daypilot-pro-angular';

Use one edition's import at a time. The shared integration does not imply that every Calendar feature is available in both editions; check the availability on the individual feature pages.

Configuration and Event Data

The [config] input accepts Calendar properties and event handlers. Keep event data in the separate [events] input. Do not also put the same collection in config.events: the events input takes ownership of the initial event list.

Each event needs a unique id, start, end and display text. The end is exclusive: an event from 10:00 to 12:00 finishes at 12:00. Events must overlap the displayed dates to be visible. The example starts on September 28 and includes an event on September 29.

Signal Inputs

Pass the signals themselves to the wrapper:

<daypilot-calendar [config]="config" [events]="events"></daypilot-calendar>

The wrapper observes the signals and applies their new values. Use set() or update() with a new object or array, as in changeWeek() and eventChanged() above. Mutating a nested field without notifying the signal does not trigger this update path.

Writing [config]="config()" or [events]="events()" passes an ordinary object or array instead and uses the plain-input behavior described below. Keep one source of truth for each collection: in these examples, update the Angular events signal when data changes.

Signals are also useful with Angular's zoneless change detection. If a DayPilot callback or asynchronous operation changes plain properties displayed elsewhere in an Angular template, use an Angular notification mechanism such as ChangeDetectorRef.markForCheck(), or store that state in signals.

Loading Calendar Event Data

Load data from your application's endpoint and assign the returned event array to the events signal. The endpoint is application-specific; DayPilot does not create it or persist edits automatically.

The following members can be added to CalendarComponent. They request the current range using visibleStart() and visibleEnd(), handle failed responses, and ignore a response if a newer request or a change of visible dates makes it stale.

loadError = signal('');
private loadRequest = 0;

async loadEvents(): Promise<void> {
  const request = ++this.loadRequest;
  const calendar = this.calendar.control;
  const start = calendar.visibleStart().toString();
  const end = calendar.visibleEnd().toString();
  const query = new URLSearchParams({start, end});
  this.loadError.set('');

  try {
    const response = await fetch(`/api/events?${query}`);
    if (!response.ok) {
      throw new Error(`Loading failed: ${response.status}`);
    }
    const events: DayPilot.EventData[] = await response.json();
    if (request !== this.loadRequest ||
        start !== calendar.visibleStart().toString() ||
        end !== calendar.visibleEnd().toString()) {
      return;
    }
    this.events.set(events);
  } catch (error) {
    if (request === this.loadRequest &&
        start === calendar.visibleStart().toString() &&
        end === calendar.visibleEnd().toString()) {
      this.loadError.set(error instanceof Error ? error.message : 'Loading failed');
    }
  }
}

Add a reload button and an error message to the template:

<button (click)="loadEvents()">Reload events</button>
<p role="status">{{ loadError() }}</p>

Return a JSON array in the same shape as the sample events data. To include events that cross a range boundary, query for events whose start is before the requested end and whose end is after the requested start. The requested end is exclusive.

Signal configuration updates are applied during Angular rendering. When automatically loading after navigation, use the intended dates from your application state, or wait until the Calendar has applied the new configuration before reading its visible range. Calling loadEvents() immediately after config.update() can still read the previous range. The reload button above reads the range after the new view is displayed.

Event Handlers and Saving Changes

Define callbacks inside config, as shown by onEventMoved and onEventResized in the complete example. The typed configuration gives each callback its argument type. Arrow functions keep access to the host component through this.

With the Update handling mode used above, Calendar applies the move or resize before calling the corresponding notification handler. The example copies the resulting dates into a new Angular event collection. Add your backend request there and handle rejected saves according to your application's rules. A successful drag changes the browser's calendar; it does not by itself save anything to a database.

Using Plain Objects and Arrays

Existing applications can also bind an ordinary configuration object and event array. This complete alternative shows that input form:

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

@Component({
  selector: 'app-plain-calendar',
  standalone: true,
  imports: [DayPilotModule],
  template: `
    <button (click)="nextWeek()">Next week</button>
    <daypilot-calendar
      [config]="config"
      [events]="events"
      (viewChange)="viewChanged()">
    </daypilot-calendar>
  `
})
export class PlainCalendarComponent {
  config: DayPilot.CalendarConfig = {
    viewType: 'Week',
    weekStarts: 1,
    startDate: '2026-09-28'
  };

  events: DayPilot.EventData[] = [
    {
      id: 1,
      start: '2026-09-29T10:00:00',
      end: '2026-09-29T12:00:00',
      text: 'Planning meeting'
    }
  ];

  nextWeek(): void {
    this.config = {
      ...this.config,
      startDate: new DayPilot.Date(this.config.startDate!).addDays(7)
    };
  }

  viewChanged(): void {
    console.log('Calendar configuration applied');
  }
}

For plain inputs, the wrapper compares JSON-serialized values when Angular checks the component. Keep the data serializable and avoid circular references. Changes to nested serializable values can be detected, but replacing only a callback function does not change the JSON representation. Set callbacks in the initial configuration, or use a signal configuration that emits a new object when replacing handlers.

The viewChange output used here is emitted when the wrapper applies a detected plain-configuration change. It is not limited to date navigation. Treat it as a notification and read the current range from the Calendar control if needed; do not interpret $event as the new date.

Signal configuration updates do not emit viewChange, and event-list changes alone do not emit it either. Use your application's navigation/loading flow with the signal example.

Accessing the Calendar API

DayPilotCalendarComponent is the Angular wrapper. Its control property is the underlying DayPilot.Calendar instance, which exposes the Calendar methods, including update().

The complete example obtains the wrapper using #calendar and @ViewChild. For an unconditionally rendered Calendar, the control is available in the host's ngAfterViewInit() and in later button handlers, such as showRange(). If the Calendar is rendered conditionally, wait until that child exists before using it.

The wrapper creates, initializes and disposes the Calendar. You do not need to call init() or dispose() yourself. Use configuration and events bindings for the state owned by Angular; use the control for operations such as reading the visible range. If you update event data through the control API, also reconcile the collection owned by your application.

DayPilot Pro also provides the message() method for an integrated message bar. This method is not available in DayPilot Lite.

Using NgModule Applications

You can use the same <daypilot-calendar> tag in an NgModule application. Import DayPilotModule from the selected DayPilot package into the NgModule that declares your Calendar host. For a non-standalone host, set standalone: false and move imports: [DayPilotModule] from the component decorator to that NgModule. Keep the same config/events bindings and TypeScript data types.

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