Scheduler
A calendar & event scheduler with month, week, day, and agenda views. Drag events to reschedule, resize to change duration, or drag across the grid to create new ones.
Installation
dotnet add package Lumeo.Scheduler
One-time app setup (AddLumeo(), CSS & JS) is covered in the
installation guide.
Usage
@using Lumeo <Scheduler />
Scheduler renders its own
Blazor views — month, week, day, N-day, agenda and resource — with no JS calendar dependency and no
CDN fetch. Everything below works offline and under a strict CSP.
The views also ship as standalone components (
SchedulerMonthView,
SchedulerTimeGridView, SchedulerAgendaView,
SchedulerResourceView) if you would rather compose your own chrome than use
Scheduler's toolbar.
When to Use
- Displaying appointments, meetings, shifts, or any time-based event grid.
- Giving users direct manipulation controls — drag to reschedule, drag-to-select to create, resize to change duration.
- Switching between month overviews, focused day/week planning, and a flat agenda list within the same data.
- Showing overlapping events (double-booked rooms, parallel tracks), multi-day spans, and recurring series side by side.
The views
Month, week/day, agenda and resource renderers, written in Blazor. Each is usable on its own, and
Scheduler composes them behind its toolbar.
SchedulerEvent.Recurrence,
including raw RRULE strings), ISO week numbers (ShowWeekNumbers), weekend
hiding (HideWeekends), live-region announcements for drag outcomes, a
configurable grid interval (SlotDuration, with
SnapDuration when drags should be finer than the grid), an N-day view
(SchedulerView.MultiDay plus VisibleDays), a
resource view (SchedulerView.Resource, one column per entry in
Resources), and a display TimeZone.
Resources colour-codes events by resource and
SchedulerView.Resource lays out one lane each, but neither composes
independent calendars. Tracked as a roadmap item, stated here so it is visible before you build
on the assumption.
Realistic month with overlapping events, a multi-day span, and a weekday-recurring standup.
Today's cell has 5 events — more than the 3-lane budget — so it overflows into a
"+N more" popover. Drag an event to move it (ghost preview until you
release); drag across empty days to create one.
Same event set, time-grid layout. Overlapping events are packed side by side, off-hours are shaded, and the red line tracks the current time. Drag moves an event across days/times as a ghost outline; drag the bottom edge to resize.
Flat chronological list across the next 30 days — the same events as the calendar views above, including recurring occurrences and multi-day spans, grouped by day.
CanDrop is a three-way gate evaluated before any drop commits:
SchedulerDropResult.Reject, .Accept, or
.AcceptWith(adjustment) to snap the drop to a different time than the
user dragged to. Nothing mutates until CanDrop returns — dragging only
ever moves a ghost outline. Try dragging the event onto the 15th (rejected) or
the 22nd (accepted, but snapped forward one day) of this month.
Using Scheduler
The views above are standalone components. Scheduler puts the same
renderers behind its own toolbar and adds the surrounding API —
InitialView, Height,
@bind-Events, OnDateSelect,
OnEventChange, prev/next/today.
Switch views with the toolbar and page with prev/next.
A 15-minute grid. The gutter still prints one label per hour and hour boundaries
keep the stronger rule, so the day still reads as hours rather than 96 stripes.
Drags snap to the grid unless SnapDuration says otherwise.
Values that do not divide an hour are rounded up to one that does — rows have to
tile an hour exactly or the labels drift away from the lines they name.
A rolling window of VisibleDays days. Unlike the week view it
does not snap to a week start, and prev/next page by the window's own width, so
consecutive pages tile the calendar instead of overlapping. The toolbar button appears
only once you have chosen a width — an N-day view with no N is not a view.
Both calendars below hold the same events, read in two zones. Only events
carrying a real instant move: a DateTime with
Kind of Utc or
Local denotes a point in time and can be re-read elsewhere,
while an Unspecified value is a wall-clock reading that is
already in whatever zone you meant — so setting a zone cannot silently shift existing
data. Edits are converted back before they reach
OnEventChange: what you get out is in the frame you put in.
All-day events are dates, not instants, and never move.
SchedulerView.Timeline puts one row per resource and
runs the time axis horizontally across days, weeks or months — the
shape Outlook's scheduling assistant has. A different question from the resource view
below: this one shows how a resource is booked over a stretch, that one shows who is
where on a single day.
Overlapping bookings stack into their own lanes rather than drawing over each other,
so a double booking is visible instead of hidden, and the row grows to fit. A booking
running in from before the window is clamped to the edge, not dropped — a resource
that is busy must never read as free.
SchedulerView.Resource lays out one column per entry in
Resources — genuinely separate lanes, not just color-coding.
BuiltInEventDialog is off by default, because most apps already have their
own appointment form wired through OnDateSelect and
OnEventClick. Turning it on never displaces that: both callbacks still fire
exactly as before, so the dialog is additive. Double-click a day to create, click an event to edit;
saving reports through EventsChanged — the same channel a drag uses.
A SchedulerCalendar is which FEED an event came from, where a
SchedulerResource is what it consumes — so calendars decide whether an
event is shown at all. Click a chip to hide one; the button beside them splits the visible
calendars into their own panes and merges them back. An event with no
CalendarId belongs to no calendar and is never hidden.
A selection drawn in a pane reports that pane on
SchedulerDateRange.CalendarId, so your own appointment form can file the
new event into the column the user drew it in. It is null while the
calendars are overlaid, where no single one owns the gesture.
API Reference
Scheduler
SchedulerEvent
| Field | Type | Description |
|---|---|---|
| Id | string | Stable identifier used to reconcile edits back into the caller's collection. |
| Title | string | The label rendered on the event chip. |
| Start | DateTime | Start timestamp (inclusive). |
| End | DateTime | End timestamp (exclusive). |
| AllDay | bool | When true the event is rendered in the all-day lane. |
| Color | string? | CSS color or variable reference like "var(--color-primary)". |
| Url | string? | Optional link opened on click instead of firing OnEventClick. |
| ExtendedProps | Dictionary<string, object>? | Arbitrary app-level metadata round-tripped through the JS layer. |
| DaysOfWeek | IReadOnlyList<DayOfWeek>? | Recurring: days of the week on which the event repeats (both engines). When set, Start/End provide the time-of-day only. |
| RecurrenceEnd | DateTime? | Recurring: last date on which the event appears. Only meaningful when DaysOfWeek is set. |
| ExceptionDates | IReadOnlyList<DateTime>? | Recurring: specific dates to skip (one-off exclusions). |
| Recurrence | SchedulerRecurrenceRule? | Structured RRULE-subset recurrence (FREQ, INTERVAL, COUNT, UNTIL, BYDAY). Takes precedence over DaysOfWeek/RecurrenceEnd when set; supported by both engines. |
| ResourceId | string? | Matches this event to a SchedulerResource for color-coding when the Scheduler has a Resources list. |
| ClassNames | string? | Extra CSS class names applied directly to this event's chip (space-separated). |
SchedulerResource
| Field | Type | Description |
|---|---|---|
| Id | string | Identifier matched against SchedulerEvent.ResourceId. |
| Title | string | Display name shown in the resource legend. |
| Color | string? | CSS color applied to events belonging to this resource when the event has no explicit color. |
SchedulerDateRange
| Field | Type | Description |
|---|---|---|
| Start | DateTime | Range start timestamp. |
| End | DateTime | Range end timestamp (exclusive). |
| AllDay | bool | True when the user selected whole days in a day-grid. |
SchedulerView
| Value | Description |
|---|---|
| Month | Day-grid month view — best for an at-a-glance overview. |
| Week | Time-grid week view with hourly rows. |
| Day | Time-grid single-day view. |
| List | Agenda-style list of the current week's events. |
View components
Not yet a registered public API surface (no dedicated docs registry entry), but usable and independently
testable today. Shared parameters across all three: Events,
AnchorDate, OnEventClick,
ResolveEventColor, Class, AdditionalAttributes.
| Component | Distinguishing parameters | Description |
|---|---|---|
| SchedulerMonthView | MaxVisibleLanes, ShowWeekNumbers, HideWeekends, SelectedDate(Changed), CanDrop, Selectable, BusinessHours(Days), Editable, OnDateSelect, OnEventChange | Always-42-cell month grid. Events beyond MaxVisibleLanes (default 3) collapse into a "+N more" popover. ShowWeekNumbers adds a leading ISO-8601 week column; HideWeekends drops the Saturday/Sunday columns (keyboard navigation steps over them). |
| SchedulerTimeGridView | Days, SlotMinTime/MaxTime, NowIndicator, CanDrop, BusinessHours(Start/End/Days), Editable, Selectable, OnDateSelect, OnEventChange | Hourly time grid. Days="7" is the week view, Days="1" is the day view. |
| SchedulerAgendaView | DaysToShow | Flat chronological list, grouped by day, read-only (no drag/resize — it's a list). |
SchedulerDropResult / CanDrop
CanDrop is a plain
Func<SchedulerEvent, SchedulerScheduleDropContext, SchedulerDropResult>
parameter on the Scheduler views. It is
evaluated before a drag/resize/create commits — nothing mutates the underlying collection until it returns.
| Member | Description |
|---|---|
| SchedulerDropResult.Reject | Cancels the drop; the event snaps back to its original position. |
| SchedulerDropResult.Accept | Commits the drop exactly as proposed. |
| SchedulerDropResult.AcceptWith(adjustment) | Commits the drop, overriding Start/End/AllDay with a SchedulerDropAdjustment (e.g. snapping to a valid slot). |
| SchedulerScheduleDropContext | ProposedStart, ProposedEnd, Source (which gesture — move, resize, or create — produced this proposal). |
Related Components
- Calendar — picker-only date grid without events.
- DatePicker — single-date input with a calendar dropdown.