Calendar
Month, week, day, agenda and year views of grid records, with recurrence, time zones and iCalendar export.
7 demos
Month, week, day, agenda and year views of grid records, with recurrence, time zones and iCalendar export.
A list of events and nothing else: month, week, day and agenda, read-only, with a details popover.
Creating events, dropping work from a grid, sources with a legend, office hours shaded and context menus.
A mini month, week and day lists, stacked months and a year with its events.
A facet panel beside the calendar, busy days folded into +N more, and a timeline per issuer at month zoom.
A directory's password expiries as an all-day agenda per person, grouped by department in the timeline.
Selecting days filters the grid, and selecting rows highlights their events in the calendar.
Build one like it
The smallest complete version, in each language CanopyX supports for this view. The demo above is the same API with more data.
using CanopyX;
using CanopyX.Html;
static DateTime At(int day, int hour, int minute = 0) =>
new(2026, 10, day, hour, minute, 0, DateTimeKind.Utc);
var calendar = new CanopyCalendar("operations", "Operations calendar", new[] {
new CanopyCalendarEvent("Core switch firmware upgrade", At(14, 22)) {
End = At(15, 3),
Tone = CanopyTone.Info
},
new CanopyCalendarEvent("Database failover test", At(16, 9)) {
End = At(16, 11),
Description = "DBA team, db-02"
},
new CanopyCalendarEvent("Disk latency on fs-01", At(16, 10, 15)) {
End = At(16, 12),
Tone = CanopyTone.Danger
},
new CanopyCalendarEvent(
"Certificate expires: portal.example.com", At(21, 13, 45)) {
Tone = CanopyTone.Warning
},
new CanopyCalendarEvent("Data centre power test", At(24, 6)) {
End = At(24, 18)
},
new CanopyCalendarEvent("Weekly maintenance window", At(6, 20)) {
End = At(6, 23),
Recurrence = CanopyRecurrence.Weekly(DayOfWeek.Tuesday)
},
new CanopyCalendarEvent("Change freeze", new DateTime(2026, 10, 26)) {
AllDay = true,
End = new DateTime(2026, 10, 31)
}
}, CanopyCalendarPreset.Standard);
calendar.Date = new DateTime(2026, 10, 16);
calendar.FirstDay = DayOfWeek.Monday;
calendar.SaveHtmlPage("calendar.html");Run this snippet (calendar, C#)Its output, generated with the site.
using CanopyX;
using CanopyX.Html;
var grid = new CanopyDataGridView<Operation>(
"operations", "Operations calendar", x => x.Id);
grid.Column(x => x.Title, configure: c => c.RowHeader = true);
grid.Column(x => x.Status, configure: c => c.Presenter = CanopyGridPresenter.State(
new Dictionary<string, CanopyTone> {
["Incident"] = CanopyTone.Danger,
["Running"] = CanopyTone.Info,
["Planned"] = CanopyTone.Neutral,
["Done"] = CanopyTone.Success
}));
grid.Column(x => x.Owner);
var calendar = grid.Calendar()
.Start(x => x.Starts)
.End(x => x.Ends)
.Title(x => x.Title)
.Subtitle(x => x.Owner)
.Status(x => x.Status)
.Preset(CanopyCalendarPreset.Standard);
calendar.Definition.Date = new DateTime(2026, 10, 16);
calendar.Definition.FirstDay = DayOfWeek.Monday;
static DateTime At(int day, int hour, int minute = 0) =>
new(2026, 10, day, hour, minute, 0, DateTimeKind.Utc);
grid.AddRows(new Operation[] {
new("op-1", "Core switch firmware upgrade", "Planned",
At(14, 22), At(15, 3), "Network"),
new("op-2", "Database failover test", "Running",
At(16, 9), At(16, 11), "DBA"),
new("op-3", "Disk latency on fs-01", "Incident",
At(16, 10, 15), At(16, 12), "Storage"),
new("op-4", "Directory schema update", "Done",
At(7, 18), At(7, 19), "Identity"),
new("op-5", "Data centre power test", "Planned",
At(24, 6), At(24, 18), "Facilities")
}).View.SaveHtmlPage("calendar.html");
internal sealed record Operation(
string Id, string Title, string Status,
DateTime Starts, DateTime? Ends, string Owner);Run this snippet (calendar, C# fluent)Its output, generated with the site.
import { createCalendar } from "@evotecit/canopyx/grid";
import "@evotecit/canopyx/grid.css";
createCalendar(document.getElementById("calendar"), {
id: "operations",
label: "Operations calendar",
preset: "standard",
date: "2026-10-16",
firstDay: 1,
events: [
{
title: "Core switch firmware upgrade", tone: "info",
start: "2026-10-14T22:00:00Z", end: "2026-10-15T03:00:00Z"
},
{
title: "Database failover test", description: "DBA team, db-02",
start: "2026-10-16T09:00:00Z", end: "2026-10-16T11:00:00Z"
},
{
title: "Disk latency on fs-01", tone: "danger",
start: "2026-10-16T10:15:00Z", end: "2026-10-16T12:00:00Z"
},
{
title: "Certificate expires: portal.example.com", tone: "warning",
start: "2026-10-21T13:45:00Z"
},
{
title: "Data centre power test",
start: "2026-10-24T06:00:00Z", end: "2026-10-24T18:00:00Z"
},
{
title: "Weekly maintenance window", recurrence: "FREQ=WEEKLY;BYDAY=TU",
start: "2026-10-06T20:00:00Z", end: "2026-10-06T23:00:00Z"
},
{ title: "Change freeze", start: "2026-10-26", end: "2026-10-31" }
]
});Run this snippet (calendar, JavaScript)Its output, generated with the site.
Try this in the demo
- Views of one record set
Switch between Month, Week, 3 days, Agenda and Year.
Every view reads the same grid records; search, filters and saved views carry across.
- Recurring windows
Find the daily stand-up and the weekly maintenance window, then open one occurrence.
Series come from a recurrence rule, with one skipped date and one moved occurrence kept as exceptions.
- Background ranges
Look at the change freeze and the company holiday in month view.
Ranges shade the days behind the events instead of competing with them.
- Record panel
Open any event.
The record opens in the panel with its ticket, owner and notes, the same panel the grid uses.