Acronis UIKit
Components

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 (13) 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

Edit on GitHub

On this page