<!-- Generated by scripts/gen-docs.mts from the JSDoc in registry/. Do not edit; run `pnpm run docs`. -->

# ThreadSummary

The line under a post that leads to its thread: who replied, "N replies" (a button that opens the thread) and when the last reply came. Before anyone has replied it is a quiet "Reply" button with a reply arrow, to start the thread, which ends a post's name line and shows on hover and focus where a mouse points.

```tsx
import { ThreadSummary } from "@/components/ouro/message/thread-summary";
```

## Example

```tsx
<ThreadSummary count={thread.replies.length} repliers={thread.repliers} lastReply={formatTime(thread.lastReplyAt)} onOpen={() => openThread(thread.id)} />
```

## Usage

Pass it to `Message`'s `thread`, under a `variant="post"` message, or a `chat` message in a channel without threads whose message has replies.

## Use instead when

- Use `Button` instead for an action that is not about a thread, and `AvatarGroup` alone to show who is in something.

## Props

Type: `ThreadSummaryProps`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `className` | `string` |  | Layout only: margins (`mt-1`). Appearance classes warn in development. |
| `count` (required) | `number` |  | How many replies the thread has. With none, the summary is a single small "Reply" button that opens the thread to write the first one. As a post's `thread`, that button sits at the end of the post's line of name and time, so it adds no row. Where a mouse or trackpad points, it shows only while the pointer is over the post or focus is in it; on a touch screen it always shows, in the same place. |
| `label` | `string` |  | What screen readers hear for the button, when its words alone ("2 replies") would repeat from post to post: "2 replies to Riley's post Settings mockups". Start it with the words the button shows, so speech input that says them still finds it (WCAG's label in name). |
| `lastReply` | `string` |  | When the last reply came, already formatted for the reader ("7:44 AM"). |
| `lastReplyDateTime` | `string` |  | The same moment for machines, as an ISO date: the `<time>` element's `dateTime`. |
| `lastReplyLabel` | `((time: string) => string)` | `` (time) => `Last reply ${time}` `` | The words around the last reply's time, for other languages. |
| `onOpen` (required) | `(event: MouseEvent<HTMLElement>) => void` |  | Opens the thread, with the click event, so the app can check `event.isTrusted`. |
| `ref` | `Ref<HTMLDivElement>` |  | Reaches the rendered element. |
| `repliers` | `readonly ThreadReplier[]` |  | Who replied, in the order to show them. The first three show as small overlapping faces: the picture, or one initial on the person's colour; the count beside them says how many replied in all. |
| `repliersLabel` | `string` | `"Replied"` | What screen readers hear for the repliers' avatars, for other languages. |
| `repliesLabel` | `((count: number) => string)` | `` (count) => count === 1 ? "1 reply" : `${count} replies` `` | The button's words for a count, for other languages. |
| `replyLabel` | `string` | `"Reply"` | The button's words before anyone has replied, for other languages. |

## Native attributes

Also accepts the attributes in React's `HTMLAttributes` (`id`, `aria-*`, event handlers), passed to the rendered element, except those named on this page.

## Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

- `onClick`: Not a prop. The thread opens with `onOpen`: `<ThreadSummary count={2} onOpen={openThread}>`.
- `replyCount`: Not a prop. The number of replies is `count`: `<ThreadSummary count={replies.length}>`.

## `className` is for layout only

Pass margins, width and grid placement (`mt-4`, `w-full`, `col-span-2`). Your classes always win over Ouro UI's: ThreadSummary merges `className` last with `cn` (tailwind-merge), so a class that conflicts with its own replaces it.[^layout-classes]

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (`hover:`, `sm:`) and any `!` are removed, it starts with `bg-`, `text-`, `border-`, `rounded-`, `shadow-`, `font-`, `ring-`, `from-`, `via-`, `to-`, `fill-` or `stroke-`, or is `border`, `rounded`, `shadow` or `ring`, or has a `dark:` variant. Text wrapping, overflow and table layout classes (`text-wrap`, `text-ellipsis`, `border-collapse`) are layout and do not warn.

Alignment classes (`text-center`) warn too: set alignment on a parent element.

## Types

### `ThreadReplier`

Someone who replied in a thread: their name, and their picture if there is one.

```ts
interface ThreadReplier {
  /** Their name, for the avatar's initials and for screen readers. */
  name: string;
  /** Their picture's URL. Without it the avatar shows initials. */
  src?: string | undefined;
}
```

[^layout-classes]: test:registry/ouro/components/ouro/message/thread-summary.test.tsx::ThreadSummary > forwards a ref and lets app layout classes win verify:testExists(registry/ouro/components/ouro/message/thread-summary.test.tsx::ThreadSummary > forwards a ref and lets app layout classes win)
