The Angular Kanban component displays work as cards arranged in workflow columns, with optional swimlanes. A card’s column records its workflow stage; its swimlane can identify the team or other group responsible for it. Users can change that placement and reorder cards using drag and drop.
This component is included in DayPilot Pro for JavaScript. It is not included in DayPilot Lite.

Angular Kanban Features
Workflow columns. Organize work into stages such as To do, In progress, and Done. Users can move cards between columns and change their order within a column.
Swimlanes. Group the same workflow by team, project, or another category. Each card identifies both its column and its swimlane. Add row header columns to display extra swimlane fields, such as a team’s location, beside the board.
Custom card data. Display titles, text, colors, and application-specific fields. Use card-rendering callbacks and active areas for additional labels and actions.
Readable columns on wide boards. Let columns share the available width, or give them a fixed width and scroll horizontally through the remaining stages.
Application integration. Load columns, cards, and swimlanes from your backend. Use the move callback to save workflow and ordering changes; the component does not supply a persistence service.
Angular Kanban Examples
Keep Workflow Stage and Team Separate
Swimlanes add a second dimension to the workflow columns. In the swimlane demo, Analysis, Draft, and Testing run across the board, and Swimlane A and Swimlane B divide it into horizontal groups. A card can move to Draft while staying in its current lane, or move to the other lane without changing its workflow stage.

Columns and swimlanes identify two independent parts of a card’s placement.
The Angular examples below apply the same arrangement to Team A and Team B. A card's column and swimlane values match IDs from the two bound collections. The move callback can save both values and the card order. The application supplies the team names, workflow stages, and persistence.
Explain a Move Before the Card Is Dropped
The moving-customization demo displays the target column and insertion position while a card is being dragged. It also shows a specific workflow rule: a card may advance to the next column, but cannot skip forward over a column. For example, moving from Analysis directly to Testing is rejected with a “Cannot skip columns” label; moving from Analysis to Draft is allowed.
The application implements this rule in onCardMoving by comparing source and target column indexes and setting args.allowed. It can supply an explanatory label on either side of the dragged card. This example still allows backward moves: the rule is defined by the callback, rather than inferred from the column names. See card moving customization for the callback and label options.
Demos and Tutorials
Live Feature Demos
These JavaScript demos show the underlying Kanban features configured through the Angular component’s [config] input. They use DayPilot Pro.
Swimlanes: move cards between workflow columns and horizontal groups.
Moving customization: show the target position and reject a move that skips forward over a column.
Fixed column width: keep each workflow column readable and use horizontal scrolling to reach additional columns.
Older Angular Projects
Angular 5 Kanban Quick Start Project (Pro)
An older Angular project with Analysis, Draft, and Testing columns, card descriptions, and colored card bars. Use the standalone integration examples below or UI Builder for a new project.Angular 4 Kanban Quick Start Project (Pro)
Historical Angular 4 Kanban example for maintaining older applications.
Configure a Starter Project
Use Kanban UI Builder to explore settings in a live preview and download an Angular starter. Continue adjusting the generated configuration in your own code.
Installation
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 Kanban Board
Start with two workflow columns and one card. Drag the card from To Do to Done.
kanban.component.ts
import {Component} from '@angular/core';
import {DayPilot, DayPilotModule} from 'daypilot-pro-angular';
@Component({
selector: 'app-kanban',
standalone: true,
imports: [DayPilotModule],
template: `
<daypilot-kanban [columns]="columns" [cards]="cards"></daypilot-kanban>
`
})
export class KanbanComponent {
columns: DayPilot.KanbanColumnData[] = [
{id: 'todo', name: 'To Do'},
{id: 'done', name: 'Done'}
];
cards: DayPilot.CardData[] = [
{id: 'proposal', name: 'Proposal', text: 'Prepare proposal', column: 'todo'}
];
}
Import KanbanComponent in its parent and add <app-kanban></app-kanban> to the parent template. Each card has a unique id, a title in name, optional body text, and a column matching a column ID.
Add Cards and Group Them by Team
Swimlanes add horizontal groups. This board has two teams. The Add card button asks for a task name and adds it to Team A’s To Do column. Every card, including new ones, carries both its column and swimlane IDs.
kanban-teams.component.ts
import {Component, ChangeDetectorRef, inject} from '@angular/core';
import {DayPilot, DayPilotModule} from 'daypilot-pro-angular';
@Component({
selector: 'app-kanban',
standalone: true,
imports: [DayPilotModule],
template: `
<button (click)="addCard()">Add card</button>
<daypilot-kanban
[columns]="columns"
[swimlanes]="swimlanes"
[cards]="cards"></daypilot-kanban>
`
})
export class KanbanComponent {
private changes = inject(ChangeDetectorRef);
columns: DayPilot.KanbanColumnData[] = [
{id: 'todo', name: 'To Do'}, {id: 'done', name: 'Done'}
];
swimlanes: DayPilot.SwimlaneData[] = [
{id: 'team-a', name: 'Team A'}, {id: 'team-b', name: 'Team B'}
];
cards: DayPilot.CardData[] = [
{id: 'proposal', name: 'Proposal', text: 'Prepare proposal',
column: 'todo', swimlane: 'team-a'}
];
async addCard() {
const modal = await DayPilot.Modal.prompt('Task name:', 'New task');
if (modal.canceled) { return; }
this.cards = [...this.cards, {
id: DayPilot.guid(), name: modal.result,
column: 'todo', swimlane: 'team-a'
}];
this.changes.markForCheck();
}
}
Replacing the bound cards array displays application-created cards. See column loading, card loading and swimlane loading for the data formats. The example adds cards locally; a backend application should create the record first and use the returned ID.
Save Card Placement and Order
Moving a card updates the board automatically. onCardMoved runs afterward, when the card list reflects its new order, column and swimlane. Save that complete list if your backend stores the whole board’s ordering.
kanban-saving.component.ts
import {Component, ChangeDetectorRef, inject} from '@angular/core';
import {DayPilot, DayPilotModule} from 'daypilot-pro-angular';
@Component({
selector: 'app-kanban',
standalone: true,
imports: [DayPilotModule],
template: `
<button (click)="addCard()">Add card</button>
<daypilot-kanban
[config]="config"
[columns]="columns"
[swimlanes]="swimlanes"
[cards]="cards"></daypilot-kanban>
`
})
export class KanbanComponent {
config: DayPilot.KanbanConfig = {
onCardMoved: args => this.saveBoard(args.control.cards.list)
};
async saveBoard(cards: DayPilot.CardData[]) {
try {
await DayPilot.Http.put('/api/board/cards', cards);
} catch {
await DayPilot.Modal.alert('The board could not be saved.');
}
}
private changes = inject(ChangeDetectorRef);
columns: DayPilot.KanbanColumnData[] = [
{id: 'todo', name: 'To Do'}, {id: 'done', name: 'Done'}
];
swimlanes: DayPilot.SwimlaneData[] = [
{id: 'team-a', name: 'Team A'}, {id: 'team-b', name: 'Team B'}
];
cards: DayPilot.CardData[] = [
{id: 'proposal', name: 'Proposal', text: 'Prepare proposal',
column: 'todo', swimlane: 'team-a'}
];
async addCard() {
const modal = await DayPilot.Modal.prompt('Task name:', 'New task');
if (modal.canceled) { return; }
this.cards = [...this.cards, {
id: DayPilot.guid(), name: modal.result,
column: 'todo', swimlane: 'team-a'
}];
this.changes.markForCheck();
}
}
The /api/board/cards endpoint should store array order as well as card placement. The example reports a failed save while leaving the local move visible; add retry or rollback behavior as needed. The swimlane bindings and the Add card behavior remain part of this board.
Access the Kanban API
Use the underlying control to find and update an existing card. This independent example renames the proposal without replacing the whole board.
kanban-api.component.ts
import {Component, ViewChild} from '@angular/core';
import {DayPilot, DayPilotModule, DayPilotKanbanComponent} from 'daypilot-pro-angular';
@Component({
selector: 'app-kanban',
standalone: true,
imports: [DayPilotModule],
template: `
<button (click)="renameCard()">Rename proposal</button>
<daypilot-kanban #kanban
[columns]="columns"
[swimlanes]="swimlanes"
[cards]="cards"></daypilot-kanban>
`
})
export class KanbanComponent {
columns: DayPilot.KanbanColumnData[] = [
{id: 'todo', name: 'To Do'}, {id: 'done', name: 'Done'}
];
swimlanes: DayPilot.SwimlaneData[] = [
{id: 'team-a', name: 'Team A'}, {id: 'team-b', name: 'Team B'}
];
cards: DayPilot.CardData[] = [
{id: 'proposal', name: 'Proposal', text: 'Prepare proposal',
column: 'todo', swimlane: 'team-a'}
];
@ViewChild('kanban') kanban!: DayPilotKanbanComponent;
async renameCard() {
const control = this.kanban.control;
const card = control.cards.find('proposal');
const modal = await DayPilot.Modal.prompt('Task name:', card.data.name);
if (modal.canceled) { return; }
card.data.name = modal.result;
control.cards.update(card);
}
}
Read kanban.control after the child initializes, such as in the button handler above. The wrapper manages the control’s lifecycle. See the Kanban API for methods and callbacks.
Use a Signal for Cards
If the application keeps cards in an Angular signal, pass the signal to [cards]. This variation uses update() to append the newly entered card.
kanban-signals.component.ts
import {Component, signal} from '@angular/core';
import {DayPilot, DayPilotModule} from 'daypilot-pro-angular';
@Component({
selector: 'app-kanban',
standalone: true,
imports: [DayPilotModule],
template: `
<button (click)="addCard()">Add card</button>
<daypilot-kanban
[columns]="columns"
[swimlanes]="swimlanes"
[cards]="cards"></daypilot-kanban>
`
})
export class KanbanComponent {
columns: DayPilot.KanbanColumnData[] = [
{id: 'todo', name: 'To Do'}, {id: 'done', name: 'Done'}
];
swimlanes: DayPilot.SwimlaneData[] = [
{id: 'team-a', name: 'Team A'}, {id: 'team-b', name: 'Team B'}
];
cards = signal<DayPilot.CardData[]>([
{id: 'proposal', name: 'Proposal', text: 'Prepare proposal',
column: 'todo', swimlane: 'team-a'}
]);
async addCard() {
const modal = await DayPilot.Modal.prompt('Task name:', 'New task');
if (modal.canceled) { return; }
this.cards.update(cards => [...cards, {
id: DayPilot.guid(), name: modal.result,
column: 'todo', swimlane: 'team-a'
}]);
}
}
Binding [cards]="cards" lets the wrapper observe the signal. Binding cards() passes its current array through the ordinary input path. Keep one form consistently; signals require Angular 16 or later.
Availability
Availability of this feature in DayPilot editions:
| Product | Lite | Pro |
|---|---|---|
| DayPilot for JavaScript | ||
| DayPilot for ASP.NET WebForms | ||
| DayPilot for ASP.NET MVC | ||
| DayPilot for Java |
DayPilot