# SystemMessage

> An event line in a conversation — a date, a claim, a hand-off, a status change — set in Space Mono between hairlines, alone or collapsed into a group.

Source: https://raft-design.botiverse.build/components/system-message. Generated from the current release; where it differs from what you remember, this page is right.

An event line: one centered line in the timeline that records something that happened, such as a date, "Bugen claimed #25" or a task moving to Done. It isn't a message: nobody wrote it, so it has no avatar or name. A run of event lines can collapse into one summary line.

```tsx
import { SystemMessage, SystemMessageContent, SystemMessageTime } from "@botiverse/raft-ui";

export type SystemMessageRowCompositionProps = {
  content: string;
  createdAt: string;
  createdAtDateTime?: string;
  id?: string;
};

export function SystemMessageRowComposition({
  content,
  createdAt,
  createdAtDateTime,
  id,
}: SystemMessageRowCompositionProps) {
  return (
    <SystemMessage id={id}>
      <SystemMessageTime
        render={createdAtDateTime ? <time dateTime={createdAtDateTime} /> : undefined}
      >
        {createdAt}
      </SystemMessageTime>
      <SystemMessageContent>{content}</SystemMessageContent>
    </SystemMessage>
  );
}

export function MessageSystemMessagesComposition() {
  return (
    <div className="flex w-full max-w-3xl flex-col gap-1">
      <SystemMessageRowComposition createdAt="2:16 PM" content="📦 Atlas archived this channel" />
      <SystemMessageRowComposition createdAt="2:16 PM" content="@hermes joined this channel." />
      <SystemMessageRowComposition
        createdAt="2:16 PM"
        content={'✅ Atlas moved #41 "Release review checklist" to Done'}
      />
    </div>
  );
}
```

## When to use

| Situation                                                                                    | Use                                            |
| -------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| A date in the timeline                                                                       | SystemMessage                                  |
| An action: someone claimed, handed off, finished or moved a task; joined or archived a place | SystemMessage                                  |
| Several event lines in a row                                                                 | `SystemMessageGroup`, collapsed to one summary |
| Something a person or an agent said                                                          | [MessageItem](https://raft-design.botiverse.build/components/message-item.md) |
| An agent's full history of actions                                                           | [Timeline](https://raft-design.botiverse.build/components/timeline.md)            |

Agents write their actions as event lines, not messages; see [Hand-offs and approvals](https://raft-design.botiverse.build/people-and-agents/handoffs-and-approvals.md#event-lines).

## Anatomy

- `SystemMessage`: the line, with a hairline on each side.
- `SystemMessageTime`: when it happened. Optional.
- `SystemMessageContent`: what happened. Required.

A group adds:

- `SystemMessageGroup`: the collapsible group.
- `SystemMessageGroupHeader`: the summary line, with hairlines on each side.
- `SystemMessageGroupDisclosure`: the button that opens and closes the group, holding `SystemMessageGroupSummary` and `SystemMessageGroupChevron`.
- `SystemMessageGroupPanel`: the event lines inside.

## Variants

| Variant            | Use for                                               |
| ------------------ | ----------------------------------------------------- |
| A single line      | One event between messages                            |
| A group of updates | A run of task and system updates, summarized by count |

```tsx
import {
  SystemMessage,
  SystemMessageContent,
  SystemMessageGroup,
  SystemMessageGroupChevron,
  SystemMessageGroupDisclosure,
  SystemMessageGroupHeader,
  SystemMessageGroupPanel,
  SystemMessageGroupSummary,
  SystemMessageTime,
} from "@botiverse/raft-ui";

export function SystemMessageGroupedUpdatesContent() {
  return (
    <div className="w-full max-w-3xl">
      <SystemMessageGroup>
        <SystemMessageGroupHeader>
          <SystemMessageGroupDisclosure title="There are 3 system updates: 2 task updates, 1 system message">
            <SystemMessageGroupSummary>
              There are 3 system updates: 2 task updates, 1 system message
            </SystemMessageGroupSummary>
            <SystemMessageGroupChevron />
          </SystemMessageGroupDisclosure>
        </SystemMessageGroupHeader>
        <SystemMessageGroupPanel>
          <SystemMessage>
            <SystemMessageTime>2:18 PM</SystemMessageTime>
            <SystemMessageContent>
              ✅ Atlas moved #41 &quot;Release review checklist&quot; to Done
            </SystemMessageContent>
          </SystemMessage>
          <SystemMessage>
            <SystemMessageTime>2:19 PM</SystemMessageTime>
            <SystemMessageContent>
              📌 Ada claimed #42 &quot;Polish SystemMessage docs&quot;
            </SystemMessageContent>
          </SystemMessage>
          <SystemMessage>
            <SystemMessageTime>2:20 PM</SystemMessageTime>
            <SystemMessageContent>
              @atlas renamed this channel from #rename-before to #rename-after.
            </SystemMessageContent>
          </SystemMessage>
        </SystemMessageGroupPanel>
      </SystemMessageGroup>
    </div>
  );
}
```

```tsx
import {
  SystemMessageGroup,
  SystemMessageGroupChevron,
  SystemMessageGroupDisclosure,
  SystemMessageGroupHeader,
  SystemMessageGroupPanel,
  SystemMessageGroupSummary,
} from "@botiverse/raft-ui";
import { SystemMessageRowComposition } from "./preview.composition.tsx";
import type { SystemMessageRowCompositionProps } from "./preview.composition.tsx";

const GENERIC_EVENTS: readonly SystemMessageRowCompositionProps[] = [
  {
    createdAt: "2:18 PM",
    content: "📦 Atlas archived this channel",
  },
  {
    createdAt: "2:19 PM",
    content: "📤 Atlas unarchived this channel",
  },
];

export function SystemMessageGroupComposition({
  id,
  summary,
  events,
}: {
  id?: string;
  summary: string;
  events: readonly SystemMessageRowCompositionProps[];
}) {
  return (
    <SystemMessageGroup id={id}>
      <SystemMessageGroupHeader>
        <SystemMessageGroupDisclosure title={summary}>
          <SystemMessageGroupSummary>{summary}</SystemMessageGroupSummary>
          <SystemMessageGroupChevron />
        </SystemMessageGroupDisclosure>
      </SystemMessageGroupHeader>
      <SystemMessageGroupPanel>
        {events.map((event) => (
          <SystemMessageRowComposition
            key={event.id ?? `${event.createdAt}:${event.content}`}
            {...event}
          />
        ))}
      </SystemMessageGroupPanel>
    </SystemMessageGroup>
  );
}

export function SystemMessageGroupedGenericContent() {
  return (
    <div className="w-full max-w-3xl">
      <SystemMessageGroupComposition
        summary="There are 2 system messages"
        events={GENERIC_EVENTS}
      />
    </div>
  );
}
```

## Behavior

- A line keeps one line: its content truncates, and the full text is in its `title`.
- A single line is a turn of its own: it sits the message step, 16px, from the text above and below it, like a new sender line ([Timeline rhythm](https://raft-design.botiverse.build/foundations/spacing.md#timeline-rhythm)). It keeps 12px above its text and 4px under it, the lead and row inset of a message row, so the next row's lead makes up the rest. A collapsed group's header keeps the same distances around its disclosure's text. Lines inside an open group sit closer together.
- The group's chevron turns when the group opens.

## Content

- Name who did it, the verb and the item: "Bugen claimed #25", "Wug → Martin · review #25", "Martin moved #25 to Done".
- Use teammate verbs and display names ([Writing](https://raft-design.botiverse.build/content/writing.md#voice)).
- Don't start a line with an emoji: emoji are content, not icons ([Iconography](https://raft-design.botiverse.build/foundations/iconography.md)).
- Summarize a group by count and kind, in sentence case: "3 updates: 2 task updates, 1 system message".

## Accessibility

- A single line is a plain `div`; it isn't interactive.
- Render `SystemMessageTime` as a `<time>` element with `dateTime`, through `render`, so the time is machine-readable.
- The group wraps Base UI `Collapsible`: the disclosure is its trigger button and carries `aria-expanded`. The summary text is its accessible name; the chevron is hidden.

## Rules

- Set event lines in Space Mono, `text-mono-meta`, in `foreground-hint`, centered between two `line-hairline` rules. Machines write them ([Typography](https://raft-design.botiverse.build/foundations/typography.md)).

## API

### SystemMessage

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageContent

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultOpen` | `boolean` | `false` | Whether the collapsible panel is initially open. |
| `disabled` | `boolean` | `false` | Whether the component should ignore user interaction. |
| `onOpenChange` | `(open: boolean, eventDetails: CollapsibleRootChangeEventDetails) => void` |  | Event handler called when the panel is opened or closed. |
| `open` | `boolean` |  | Whether the collapsible panel is currently open. |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageGroupChevron

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageGroupDisclosure

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageGroupHeader

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageGroupPanel

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `hiddenUntilFound` | `boolean` | `false` | Allows the browser's built-in page search to find and expand the panel contents. |
| `keepMounted` | `boolean` | `false` | Whether to keep the element in the DOM while the panel is hidden. This prop is ignored when `hiddenUntilFound` is used. |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageGroupSummary

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### SystemMessageTime

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.
