Agenda
Composable calendar with day, week, and month views for displaying and managing events with drag interactions
Import
import { Agenda, useAgenda } from '@darkcode-ui/react';Usage
Pass an events array and the mutation callbacks you want to enable. Drag-to-create, drag-to-move, drag-to-resize, and keyboard deletion are each activated only when the matching callback is provided — omit a callback and its interaction is disabled.
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import type {CalendarDateTime} from "@internationalized/date";
Anatomy
Rendering <Agenda events={...} /> without children produces the complete default layout. Every part is also exported for full composition:
import {Agenda} from '@darkcode-ui/react';
export default () => (
<Agenda events={events}>
<Agenda.Header>
<Agenda.Heading />
<Agenda.ViewSelector />
<Agenda.Navigation>
<Agenda.NavButton slot="previous" />
<Agenda.TodayButton />
<Agenda.NavButton slot="next" />
</Agenda.Navigation>
</Agenda.Header>
<Agenda.Body>
{/* Day / Week views */}
<Agenda.TimeGrid>
<Agenda.WeekHeader />
<Agenda.AllDaySection />
</Agenda.TimeGrid>
{/* Month view */}
<Agenda.MonthGrid />
</Agenda.Body>
</Agenda>
)For state shared with your own UI, build the state with useAgenda and pass it through the state prop:
const agenda = useAgenda({events, defaultView: "week"});
return (
<>
<span>{agenda.visibleDays.length} day(s) visible</span>
<Agenda state={agenda} />
</>
);Views
The agenda supports three views — day, week, and month — switched through Agenda.ViewSelector or programmatically via setView. Clicking a date number in the month grid navigates to that date in day view, and the first day of each month shows the month name (e.g. "May 1").
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import {Agenda} from "@darkcode-ui/react";Events
Events are plain objects using CalendarDateTime from @internationalized/date:
interface AgendaEvent {
id: string;
title: string;
start: CalendarDateTime;
end: CalendarDateTime;
color?: string;
isAllDay?: boolean;
isReadOnly?: boolean;
status?: "confirmed" | "unconfirmed";
}Events whose start and end fall on different dates, or with isAllDay: true, render as spanning bars in the all-day section (day/week views) or across month rows.
Drag Interactions
All drag interactions activate after an 8px pointer threshold, so plain clicks never trigger previews:
- Drag to create — drag on an empty time slot; a dashed preview snaps to
slotDuration - Drag to move — drag an event vertically to change its time, or horizontally to move it to another day
- Drag to resize — drag the handle at the bottom edge of an event
- Month move — drag a month-view event onto another cell; the target highlights via
data-drop-target
Pressing Escape cancels an in-flight drag. On touch devices, event cards set touch-action: none only when draggable, while day columns keep pan-y so scrolling always wins over drag-to-create.
Selection and Deletion
Clicking an event selects it (data-selected), clicking empty space deselects, and Escape clears the selection. When onEventDelete is provided, Delete or Backspace removes the selected event. Selection state can be controlled via selectedEventId and onEventSelect.
"use client";
import type {AgendaEvent, AgendaView} from "@darkcode-ui/react";
import type {CalendarDate} from "@internationalized/date";
Read Only
Omit the mutation callbacks for a display-only agenda, or set isReadOnly: true on individual events to keep them selectable while disabling move/resize and hiding the resize handle.
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import {Agenda} from "@darkcode-ui/react";Unconfirmed Events
status: "unconfirmed" renders an event with a dashed border and transparent background to indicate a tentative booking.
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import {Agenda} from "@darkcode-ui/react";Custom Hours and Slot Duration
startHour and endHour clip the visible time range; slotDuration controls drag snapping granularity in minutes.
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import type {CalendarDateTime} from "@internationalized/date";
All-Day Events
All-day and multi-day events stack in a collapsible section pinned above the time grid. When collapsed, per-day event counts are shown — customize them with collapsedLabel on Agenda.AllDaySection.
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import {Agenda} from "@darkcode-ui/react";Event Colors
The color prop accepts semantic token names (accent, success, warning, danger, default) or any raw CSS color. Backgrounds are derived with color-mix, so a single value themes the whole card.
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import {Agenda} from "@darkcode-ui/react";Current Time Indicator
When today is visible, a live indicator marks the current time: a badge in the time column, a faded line across all columns, and a solid segment over today's column. Hour labels near the indicator hide automatically to avoid overlap, and the position updates every minute.
Weekend Highlighting
Saturday and Sunday columns and month cells receive a subtle tint via the data-weekend attribute, computed from the active locale.
Custom Styles
"use client";
import type {AgendaEvent} from "@darkcode-ui/react";
import {Agenda} from "@darkcode-ui/react";Styling
CSS Variables
Sizing is controlled with CSS custom properties on .agenda:
| Variable | Default | Description |
|---|---|---|
--agenda-slot-height | 60px | Height of one hour in the time grid. |
--agenda-time-column-width | 58px | Width of the hour labels column. |
--agenda-current-time-color | var(--color-danger) | Color of the current time indicator. |
--agenda-event-radius | var(--radius-md) | Border radius of event cards. |
--agenda-week-header-height | 3.5rem | Height of the sticky day headers row. |
--agenda-all-day-row-height | 1.625rem | Height of one all-day/spanning event row. |
Customizing the component classes
@layer components {
.agenda {
--agenda-slot-height: 90px;
--agenda-current-time-color: var(--color-accent);
}
.agenda__event[data-selected="true"] {
@apply shadow-lg;
}
.agenda__day-column[data-today="true"] {
@apply bg-accent-soft/20;
}
}CSS Classes
Agenda uses these classes in packages/styles/components/agenda.css:
.agenda- Root container; defines the CSS variables above..agenda__header- Flex container for heading, view selector, and navigation..agenda__heading- Month/year title..agenda__view-selector- View switcher (built on ToggleButtonGroup)..agenda__navigation/.agenda__nav-button/.agenda__today-button- Navigation controls..agenda__body- Bordered content area below the header..agenda__week-header/.agenda__day-header- Sticky day headers with name and date..agenda__time-grid- Scrollable grid container..agenda__time-labels/.agenda__time-label- Hour labels column..agenda__day-column/.agenda__time-slot- Day columns and hour rows..agenda__event/.agenda__event-title/.agenda__event-time- Timed event card..agenda__resize-handle- Bottom resize handle..agenda__all-day-section/.agenda__all-day-toggle/.agenda__all-day-label/.agenda__all-day-event/.agenda__all-day-summary- All-day section parts..agenda__current-time-indicator/.agenda__current-time-badge/.agenda__current-time-line/.agenda__current-time-today-line- Current time indicator parts..agenda__month-grid/.agenda__month-weekday-header/.agenda__month-row/.agenda__month-cell- Month view structure..agenda__month-cell-date/.agenda__month-cell-more/.agenda__month-event/.agenda__month-spanning-event- Month view content..agenda__create-preview/.agenda__drop-preview- Drag previews.
Interactive States
- Selected:
[data-selected="true"]on event elements - Unconfirmed:
[data-status="unconfirmed"]— dashed border, transparent background - Read only:
[data-readonly="true"] - Dragging:
[data-dragging="true"]on the source event and the root - Resizing:
[data-resizing="true"] - Today:
[data-today="true"]on day headers, columns, and month cells - Weekend:
[data-weekend="true"]on columns and month cells - Outside month:
[data-outside-month="true"]on month cells - Drop target:
[data-drop-target="true"]on the hovered month cell during a drag
API Reference
useAgenda
The headless hook that owns all agenda state. Agenda accepts the same options directly as props, or a prebuilt state via state.
| Option | Type | Default | Description |
|---|---|---|---|
events | AgendaEvent[] | - | Events to display. Required. |
defaultView | "day" | "week" | "month" | "week" | Initial view (uncontrolled). |
view | "day" | "week" | "month" | - | Controlled view state. |
onViewChange | (view: AgendaView) => void | - | Called when the view changes. |
defaultDate | CalendarDate | today | Initial focused date (uncontrolled). |
date | CalendarDate | - | Controlled date state. |
onDateChange | (date: CalendarDate) => void | - | Called when the date changes. |
startHour | number | 0 | First visible hour in the time grid. |
endHour | number | 24 | Last visible hour in the time grid. |
slotDuration | number | 60 | Slot snapping granularity in minutes. |
onEventCreate | ({start, end}) => void | - | Enables drag-to-create. |
onEventMove | (id, start, end) => void | - | Enables drag-to-move (including cross-day and month moves). |
onEventResize | (id, start, end) => void | - | Enables drag-to-resize. |
onEventDelete | (id: string) => void | - | Enables Delete/Backspace removal of the selected event. |
onEventSelect | (id: string | null) => void | - | Called when selection changes. |
selectedEventId | string | null | - | Controlled selection state. |
The returned state includes view, date, setView, setDate, navigateNext, navigatePrevious, navigateToday, visibleDays, visibleWeeks, getEventsForDay(day), allDayLayout, getMonthRowLayout(week), getPerCellEvents(day), selectEvent, deleteSelectedEvent, and dragState.
Composition Parts
| Component | Description |
|---|---|
Agenda.Header | Container for heading, view selector, and navigation. Renders all three by default. |
Agenda.Heading | Localized month/year title (e.g. "May 2026"). |
Agenda.ViewSelector | Day/Week/Month segmented control; accepts a labels map for localization. |
Agenda.Navigation | Wrapper for nav and today buttons. |
Agenda.NavButton | Previous/next button (slot="previous" or slot="next"). |
Agenda.TodayButton | Navigates to today's date. |
Agenda.Body | Renders the time grid or month grid based on the active view. |
Agenda.TimeGrid | Scrollable day/week grid; owns the drag interactions. |
Agenda.WeekHeader | Sticky row of day headers. |
Agenda.AllDaySection | Collapsible all-day events section (collapsedLabel prop). |
Agenda.AllDayLabel | "all-day" label text. |
Agenda.AllDayEvent | Spanning bar (event, colStart, colSpan, row). |
Agenda.TimeLabels | Hour labels column. |
Agenda.CurrentTimeIndicator | Live time marker; updates every minute. |
Agenda.DayColumn | One day's column (date prop); renders that day's events by default. |
Agenda.Event | Timed event card (event prop) with resize handle. |
Agenda.MonthGrid | Month view container; renders weeks by default. |
Agenda.MonthWeekdayHeader | Weekday names row. |
Agenda.MonthRow | One week row (week prop for default rendering). |
Agenda.MonthSpanningEvent | Multi-day bar (event, colStart, colSpan, row). |
Agenda.MonthCell | Day cell (date, maxEvents, moreLabel, spanningRowCount). |
Agenda.MonthEvent | Per-cell event chip with drag-to-move support. |
Agenda.MonthCell Props
| Prop | Type | Default | Description |
|---|---|---|---|
date | CalendarDate | - | The date for this cell. Required. |
maxEvents | number | 2 | Visible events before overflow; spanning rows above the cell consume this budget. |
moreLabel | (count: number) => string | "N more" | Label for the overflow link, which navigates to day view. |
spanningRowCount | number | 0 | Spanning event rows above this cell (reserves space). |
Accessibility
- Events are focusable with descriptive labels (title, date, time range, and status); Enter/Space selects.
- Shift+ArrowUp/ArrowDown resizes a focused event by one slot when resizing is enabled.
- Month date numbers and overflow links are buttons that announce the full date.
- Week start, weekday names, weekend detection, and all time formats follow the active locale via
I18nProvider.
Related packages
@internationalized/date—CalendarDateandCalendarDateTimetypes used by all agenda propsI18nProvider— override locale for a subtree