Source: https://monarchui.dev/components/agenda-list/

Components · Date, time and scheduling

# AgendaList

Events grouped by day with sticky day headings, a Today marker and an empty state.

@rookcom/monarch-react/scheduling

Component

## Agenda

## Usage examples

### Empty

### Custom empty state and range

## Purpose

Lists upcoming events in time order, one section per day, for an agenda view, a dashboard card or a narrow screen where a grid does not fit. Needed by: calendar.

### Use when

- The agenda view of a calendar, or the phone layout of a week.
- An "Upcoming" card listing the next bookings, interviews or deadlines.

### Avoid when

- Overlaps and free time matter: use TimeGrid.
- The user scans a whole month: use CalendarMonthView.
- The rows are not events (a task list, a feed): use a plain list.

## Design principles

- Days are sections with a real heading, so the outline and screen-reader heading navigation follow the days.
- All-day events come first in a day, then timed events by start time.
- A time column on the left keeps titles aligned; below 24rem it moves above the title.
- Today is marked by text ("Today") and colour, never colour alone.
- The product supplies data and callbacks; the list only orders, groups and formats.

## API reference

```tsx
import { AgendaList } from '@rookcom/monarch-react/scheduling';
```

| Export | Kind | Rendering | Root class |
| --- | --- | --- | --- |
| `AgendaList` | component | client | `.mn-agenda-list` |

Renders a `<div>` (ref) containing one `<section aria-labelledby>` per day with an `h3` (see `headingLevel`) and a `<ul>` of EventChip `bar` chips.

Days come from the events (`from` to the last event) or from `from` + `days`; `showEmptyDays` lists days with nothing.

Times are formatted with Intl in `timeZone` and `locale`; pass both (and `today`) for identical server and client output.

Generated from the TypeScript declarations of `packages/react/src/components/scheduling/agenda-list`.

## Properties

### `AgendaList`

Events grouped by day, in order: sticky day headings with a count and a "Today" marker, all-day events first, an empty state when nothing is scheduled, and arrow keys to move between events.

| Prop | Type | Description |
| --- | --- | --- |
| `events` required | `readonly CalendarEvent[]` |  |
| `className` | `string` |  |
| `dayNotes` | `((day: string) => readonly ReactNode[] \| undefined)` | Informational rows shown first in a day, such as time off or a holiday. A day with notes is listed even when it has no events. |
| `days` | `number` | How many days from `from` to cover. Default: through the last event. |
| `empty` | `ReactNode` | Replaces the default empty state. |
| `from` | `string` | First day listed, `'2026-11-04'`. Default: the first day with an event. Events that started earlier but are still running appear on it. |
| `headingLevel` | `3 \| 4 \| 6 \| 2 \| 5` | Level of the day headings. Default 3. |
| `hourCycle` | `HourCycle` | Force a 12- or 24-hour clock (`'12'` or `'24'`, as in the date pickers); by default the locale decides. |
| `id` | `string` |  |
| `labels` | `AgendaListLabels` |  |
| `locale` | `string` |  |
| `onEventClick` | `((event: CalendarEvent) => void)` |  |
| `renderEvent` | `((event: CalendarEvent, chip: ReactElement) => ReactNode)` | Wrap or replace each chip (for example in a PopoverTrigger). Keep one focusable element. |
| `showEmptyDays` | `boolean` | List days in the range that have nothing. Default `false`. |
| `timeZone` | `string` | IANA zone events are grouped in. Defaults to the runtime's; pass it for server rendering. |
| `today` | `string \| null` | Today, for the marker. Inject for server rendering; read from the clock after mount when left out; `null` for none. |

## Variants

| Prop | Values | Description |
| --- | --- | --- |
| `headingLevel` | `'2'``'3'``'4'``'5'``'6'` | Level of the day headings, to fit the page outline. Default 3. |
| `showEmptyDays` | `'true'``'false'` | List every day in the range, with a zero count, instead of only days with events or notes. |

## States

| State | Exposed as | Behaviour |
| --- | --- | --- |
| Today | `data-today (section)` | Link-coloured date and a "Today" pill in the heading. |
| Empty | `data-empty (root)` | EmptyState "Nothing scheduled", or your `empty` node. |
| Note rows | – | Hatched informational rows from `dayNotes` (time off, holidays), shown first. |
| Event status | – | Each row is an EventChip, so tentative, cancelled and declined read as in every view. |

## Interaction behaviour

- Activating a row calls `onEventClick(event)`, or follows the event’s `href`.
- `renderEvent(event, chip)` wraps a row, for example in a PopoverTrigger.
- Give the list a height or max-height to scroll inside a card; the day headings stick to the top.
- A multi-day event appears on each day it touches; a timed event continuing from an earlier day shows "– end".

## Keyboard support

| Keys | Action |
| --- | --- |
| Tab | Enters the list at today’s first event (or the first event), then leaves it: one tab stop. |
| ArrowDown / ArrowUp | Next or previous event, across days. |
| Page Down / Page Up | First event of the next or previous day. |
| Home / End | First or last event. |
| Enter / Space | Activates the focused event. |

## Accessibility

Role

region per day (section labelled by its heading)

Accessible name

Each day section is named by its heading: "Wednesday 4 November, Today, 3 events".

- Rows are EventChip buttons named "title, time, status, detail".
- Roving tabindex keeps a long agenda to one tab stop while every event stays reachable.
- Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/agenda-list.

## Responsive behaviour

- Uses a container query: below 24rem the time sits above the title and the status beside it.
- Titles and details truncate; nothing forces horizontal scrolling at 320px.

## Theme support

- Headings use `--mn-color-bg-surface`, so they cover scrolled rows in every theme.
- The Today pill uses `--mn-color-accent-subtle` with default text, which meets contrast in every theme.
- Note rows are hatched with `--mn-color-bg-muted` / `--mn-color-bg-subtle`.

## Do and don't

### Do

- Pass `timeZone`, `locale` and `today` from the user’s settings so grouping matches the rest of the product.
- Give it a bounded height inside cards so the sticky headings work.
- Use `dayNotes` for time off and holidays instead of fake events.

### Don't

- Do not sort or group the events yourself; pass them in any order.
- Do not nest interactive controls inside a row; open details in a popover or page.

## Related components

- [EventChip](https://monarchui.dev/components/event-chip/): Each row is a `bar` EventChip.
- [CalendarMonthView](https://monarchui.dev/components/calendar-month-view/): The month layout of the same events.
- [TimeGrid](https://monarchui.dev/components/time-grid/): The day and week layout, with overlaps and free time.
- [EmptyState](https://monarchui.dev/components/empty-state/): The default empty state.

## Dependencies

| Kind | Depends on |
| --- | --- |
| npm packages | `react` |
| Monarch components | [EmptyState](https://monarchui.dev/components/empty-state/), [EventChip](https://monarchui.dev/components/event-chip/) |

## Testing coverage

- Groups by day with headings, counts and a Today marker; all-day events first, then by start time.
- Arrow keys, Page Up/Down and Home/End move between events across days; one tab stop; Enter activates.
- The tab stop starts at today or later.
- Groups in the given time zone; continuing events show their end only.
- Fixed ranges with `showEmptyDays`, `dayNotes`, heading level and labels.
- Default and custom empty states; `renderEvent` wrapping.
- No axe violations in jsdom; renders on the server.

| Check | Covered |
| --- | --- |
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/agenda-list; Cross-browser axe: /components/agenda-list |

Tests: `packages/react/src/components/scheduling/agenda-list`

## All example code

### Agenda

```tsx
import { AgendaList, type CalendarEvent } from '@rookcom/monarch-react/scheduling';

const at = (day: string, time: string) => `${day}T${time}:00Z`;

const EVENTS: CalendarEvent[] = [
  { id: 'e1', title: 'Team offsite', start: '2026-11-04', allDay: true, color: '#16a34a' },
  {
    id: 'e2',
    title: 'Stand-up',
    start: at('2026-11-04', '09:00'),
    end: at('2026-11-04', '09:15'),
    color: '#0f766e',
  },
  {
    id: 'e3',
    title: 'Design review',
    start: at('2026-11-04', '11:00'),
    end: at('2026-11-04', '12:00'),
    detail: 'Room 4',
    color: '#7c3aed',
  },
  {
    id: 'e4',
    title: 'Vendor call',
    start: at('2026-11-05', '14:00'),
    end: at('2026-11-05', '14:30'),
    status: 'tentative',
    detail: 'Video call',
    color: '#2563eb',
  },
  {
    id: 'e5',
    title: 'Retrospective',
    start: at('2026-11-06', '16:00'),
    end: at('2026-11-06', '17:00'),
    status: 'cancelled',
    color: '#c2410c',
  },
];

const base = { timeZone: 'UTC', locale: 'en-GB', today: '2026-11-04' } as const;

<div style={{ width: '100%', maxWidth: 560 }}>
  <AgendaList
    {...base}
    events={EVENTS}
    aria-label="Agenda"
    dayNotes={(day) => (day === '2026-11-05' ? ['Time off · Priya'] : undefined)}
    style={{ maxHeight: 360 }}
  />
</div>
```

### Empty

```tsx
import { AgendaList } from '@rookcom/monarch-react/scheduling';

const base = { timeZone: 'UTC', locale: 'en-GB', today: '2026-11-04' } as const;

<div style={{ width: '100%', maxWidth: 560 }}>
  <AgendaList {...base} events={[]} />
</div>
```

### Custom empty state and range

```tsx
import { Button } from '@rookcom/monarch-react';
import { AgendaList } from '@rookcom/monarch-react/scheduling';

const base = { timeZone: 'UTC', locale: 'en-GB', today: '2026-11-04' } as const;

<div style={{ width: '100%', maxWidth: 560 }}>
  <AgendaList
    {...base}
    events={[]}
    from="2026-11-09"
    days={7}
    empty={
      <div style={{ display: 'grid', gap: 8, justifyItems: 'start', padding: 16 }}>
        <p style={{ margin: 0 }}>No bookings next week.</p>
        <Button size="sm">Share booking link</Button>
      </div>
    }
  />
</div>
```
