Conversation Timeline
height animates, so the rail never resizes sideways, and it re-anchors its own scroll position while the rows grow so the list never jumps. On touch devices, where there is no hover, it stays expanded so the labels remain reachable.Conversation Timeline
Hover the rail (or Tab into it) to expand every row at once. Then hover a specific row for its extra emphasis, and click a mark to select it.
checkpoints- is the list of
{ id, label, role? }— one mark per entry, in order. activeId- is the selected checkpoint; its mark takes the accent colour.
onSelectreceives the id of a clicked mark.
Installation
New project? Follow the installation guide to set up Tailwind CSS, shadcn, the cn helper and lucide-react, register the @algorize-ui registry once, then copy your styles from the Themes page.
Install Conversation Timeline
components/ is written into your project. Copy any snippet from a Usage tab above and it runs.
Install dependencies
Nothing beyond the shared setup — Conversation Timeline needs only the cn helper and lucide-react, which the installation guide already put in place.
Examples
Left side
- Set
side"left" - to put the labels on the left of the marks — for a rail that hugs the right edge of a chat.
accent- recolours the active row's line — any CSS colour. Here the violet from the original example; pick another theme colour above to override it.
Paired with a chat
The rail as a minimap — clicking a mark scrolls that message into view, and clicking a message moves the mark.
onSelect- is where the rail earns its keep: wire it to scroll, highlight or load the message it points at.
- Set
showLabels{false} - below the
smbreakpoint so the rail shrinks to a marks-only minimap and the chat keeps the width. Resize to a phone to see it.
Density
Drag the sliders, then hover the rail to see the new sizes.
rowHeight- is each row's height once expanded (default
24);collapsedRowHeightis its height at rest (default10). visibleCount- caps how many rows show before the rail scrolls (default
10) — the faded edges appear only when there is more to scroll to.labelWidthis the fixed column labels truncate against (default240).
Features
One property animates
Only row height transitions; the mark length, gap and label width are fixed, so the rail never shifts sideways and hover can never nudge the marks.
Scroll stays put
While rows grow from 10px to 24px the rail re-anchors to the row at the top every frame, so a scrolled list expands in place instead of jumping.
Touch and keyboard
Without hover the rail stays expanded so labels remain reachable; focus expands it too, and every mark is a real button with an accessible name.
Respects reduced motion
Every transition is dropped under prefers-reduced-motion; the rail simply snaps between its two states.
Best Practices & Guidelines
Keep labels short
Labels truncate at labelWidth. Use the first line of a message or a summary rather than the full text.
Leave room to expand
The rail's height grows from visibleCount × collapsedRowHeight to visibleCount × rowHeight on hover. Place it where that growth won't push other content around.
Props Reference
<ConversationTimeline> Props
| Prop | Type | Default | Description |
|---|---|---|---|
checkpoints | TimelineCheckpoint[] | Required | One mark per entry, in order. |
activeId | string | — | Currently selected checkpoint id — its mark takes the accent colour. |
onSelect | (id: string) => void | — | Fires with the id of a clicked mark. |
side | "left" | "right" | "right" | Which side of the marks the labels sit on. |
rowHeight | number | 24 | Per-row height once expanded (hovering / focusing the rail), in px. |
collapsedRowHeight | number | 10 | Per-row height at rest — just the marks, tightly packed. |
visibleCount | number | 10 | Rows visible before scrolling; the rail's height is visibleCount × rowHeight. |
labelWidth | number | 240 | Fixed label column width in px — labels truncate past it. |
showLabels | boolean | true | Render the label column. Set false where width is tight and the rail becomes a marks-only minimap — each mark keeps its label as its accessible name. |
accent | string | "var(--primary)" | Colour of the active row's line. Any CSS colour. |
className | string | — | Merged onto the rail. |
TimelineCheckpoint
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier — also the React key. |
label | string | Text revealed when the rail expands; also the mark's accessible name. |
sublabel | string | Optional — reserved for richer labels. |
role | "user" | "assistant" | "system" | Optional role tag, handy for styling the conversation the rail points at. |