Timeline
A chronological event tree — activity feed, audit log, or status history.
Usage
import { Timeline } from '@acronis-platform/ui-react';Timeline renders a chronological tree of events as a semantic ordered list
(<ol>/<li>). Each row pairs an Avatar marker with a Card carrying the
title, an optional tag, the timestamp, and a description; rows join their parent
through an elbow and their next sibling through a vertical connector.
It is purely presentational: it never sorts, groups, fetches, or interprets
events and ships no domain event types or icons — the consumer passes ordered
Timeline.Items with a resolved icon, color, and a formatted timestamp.
It is not a temporal chart — use a Line / Area / Composed chart for trends. It is also not a real tree widget: nesting here is visual only and is not exposed to assistive tech.
Examples
<Timeline>
<Timeline.Item
icon={<TriangleWarningIcon />}
color="yellow"
title="Backup success rate dropped"
tag={<Tag variant="warning">Warning</Tag>}
timestamp={<time dateTime="2026-07-24T10:35:00+02:00">24 Jul, 10:35</time>}
description="Success rate fell from 96% to 72%"
/>
<Timeline.Item
level={2}
icon={<CircleInfoIcon />}
title="Three workloads missed their window"
description="db-01, db-02, files-07"
/>
</Timeline>Nesting
level (1–3) sets a row's indent. Nesting is flat and explicit: the
component never derives depth from JSX nesting, so render one Timeline.Item per
visible row, in visual order, and declare its level.
Everything else follows from that sequence. A row deeper than the one above it
opens a branch, so Timeline draws the elbow joining it to its parent's
connector (Figma's Nesting -First) — you don't declare the jump twice. And a
row's descending line is drawn only when the next visible row is at its own depth
or deeper, sits in the same marker column, and has an elbow to meet — so a
branch's last row, the list's last row, and a row whose descendants were just
collapsed never leave a line dangling in the margin.
<Timeline>
<Timeline.Item title="Root" />
<Timeline.Item level={2} title="First child" />
<Timeline.Item level={2} title="Last child" />
<Timeline.Item title="Next root" />
</Timeline>connector and branchStart are escape hatches for that derivation — set them
only to force a line or an elbow on or off. They are resolved as a pair, so
refusing one half also drops the other rather than leaving it unattached.
branchStart is ignored at level={1}, which has no parent to join.
Collapsing
Collapsing is the variant, not a per-row flag — there is no collapsible prop.
variant="default" never collapses anything.
Under variant="tree", every row that has descendants gets a disclosure button
ahead of its marker, which drops every following row deeper than its own. No wiring
needed, because Timeline reads its children's levels:
<Timeline variant="tree">
<Timeline.Item title="Retention policy applied" />
<Timeline.Item level={2} title="Archive pruned" />
<Timeline.Item level={2} title="Index rebuilt" />
<Timeline.Item title="Next root event" />
</Timeline>The first row derives a control because a level-2 row follows it; the two level-2 rows and the last root row are leaves, so they get none. A collapsed row keeps its control — "has descendants" is read from the rows you passed, not the visible ones — so a branch can always be reopened.
Pass expanded + onExpandedChange to own the state yourself, and
defaultExpanded={false} to start collapsed. Items must be direct children of
Timeline — wrapping them in a fragment hides their level from the root.
That variant also widens the indent step, so levels still align once the extra button is reserved.
toggleLabel is the control's accessible name. It defaults to an English string,
so pass a localized value.
A tree row can carry this control and the card chevron below at once, so
their defaults name their own action — toggleLabel is "Toggle nested events"
and bodyToggleLabel is "Toggle event details". Keep them distinct when you
localize, or a screen-reader user gets two identically-named buttons in the same
row doing different things.
Expandable card
Separately from the variant, collapsibleBody gives a row's card a chevron at
the trailing edge of its header (Figma's Action Button) that folds that card's own
body. It is the card's control, not the timeline's:
<Timeline>
<Timeline.Item collapsibleBody title="Nightly protection plan failed">
<div>Affected workloads: db-01, db-02, files-07</div>
</Timeline.Item>
<Timeline.Item level={2} initials="AA" title="Acknowledged" />
</Timeline>The two axes are orthogonal: collapsibleBody behaves the same under default and
tree, and a tree row with descendants can carry both — the branch button ahead of
the marker drops the rows below, the header chevron folds this card. Collapsing a
branch never hides a row's own body.
Pass bodyExpanded + onBodyExpandedChange to own that state, and
defaultBodyExpanded={false} to start folded. bodyToggleLabel names the control.
This lives on Timeline.Item only until Card grows the behaviour itself.
Marker
The marker is an Avatar tinted by color (blue by default; gray, green,
teal, violet, red, yellow, orange are also available), holding either an
icon or initials — pass initials for people-centric feeds, icon for system
events. Avatar's 2px outset ring is switched off here, matching the design's
strokeless marker.
The marker is decorative, so its color and glyph must never be the only carrier of
meaning — put that in the text or a Tag.
Compose Tag into tag, and anything you like as an item's children, which
renders in the card below a divider (and is hidden while the row is collapsed).
Theming
The connector and elbow use --ui-timeline-connector-color; spacing and the
indent step derive from --ui-timeline-gap, --ui-gap-16, --ui-gap-8,
--ui-avatar-global-avatar-size, and
--ui-button-icon-global-container-height. The marker, disclosure button, and
card are themed by Avatar's, ButtonIcon's, and Card's own token tiers.
The Figma variables components/Timeline/{connectorColor,gap} ship as the
--ui-timeline-* tier and are consumed directly. Timeline/gap covers the
horizontal marker-to-card gap and the indent step derived from it; the vertical
rhythm between rows is unbound in the design, so it stays on --ui-gap-16.
API Reference
Prop
Type
Prop
Type