Avara UI

Conversation Timeline

A dense rail of thin marks, one per message, that expands into a labelled list on hover.
At rest it is a compact stack of hairlines — a minimap of the whole conversation that takes almost no room beside a chat. Hover the rail (or Tab into it) and every row grows at once while the labels fade in; hover a specific row and it gets extra emphasis — a brighter mark, a darker label, a small nudge. Click any mark to select it. Only 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.
Explore the different variations of the Conversation Timeline — each example shows a live preview with the exact code to copy. Hover a rail to expand it.

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. onSelect receives 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

npx shadcn@latest add @algorize-ui/conversation-timeline

components/custom/blocks/ConversationTimelineBlock.tsx 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

Setside"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.
SetshowLabels{false}
below the sm breakpoint 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); collapsedRowHeight is its height at rest (default 10).
visibleCount
caps how many rows show before the rail scrolls (default 10) — the faded edges appear only when there is more to scroll to. labelWidth is the fixed column labels truncate against (default 240).
Row height (expanded)24px
Row height (rest)10px
Visible rows10
Label width240px

Features

Discover the Conversation Timeline's capabilities as a minimap for long conversations.

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

PropTypeDefaultDescription
checkpointsTimelineCheckpoint[]RequiredOne mark per entry, in order.
activeIdstring—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.
rowHeightnumber24Per-row height once expanded (hovering / focusing the rail), in px.
collapsedRowHeightnumber10Per-row height at rest — just the marks, tightly packed.
visibleCountnumber10Rows visible before scrolling; the rail's height is visibleCount × rowHeight.
labelWidthnumber240Fixed label column width in px — labels truncate past it.
showLabelsbooleantrueRender 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.
accentstring"var(--primary)"Colour of the active row's line. Any CSS colour.
classNamestring—Merged onto the rail.

TimelineCheckpoint

FieldTypeDescription
idstringUnique identifier — also the React key.
labelstringText revealed when the rail expands; also the mark's accessible name.
sublabelstringOptional — reserved for richer labels.
role"user" | "assistant" | "system"Optional role tag, handy for styling the conversation the rail points at.