Steps

A collapsible timeline of a run — reasoning, tool calls, and answered questions as chronological steps.

Read the request
Found three relevant sources and skimmed each.
Writing the answer

Usage guidelines

  • Recursive disclosure tree — every node is a Steps.Item with a Trigger and a Panel, and panels can hold further items, so timelines nest arbitrarily.
  • Status-driven — each item's status (complete / active / pending) flows to its Icon and Label via context; active items open by default.
  • Nesting — a nested item surfaces data-nested for the indent rail; a static row is just an Icon + Label in a <div>.
  • You compose the rows — the primitive ships the disclosure + status plumbing; row content (icons, tool-call summaries) is yours to render.
  • Get started — see Quick start to add the package.

Anatomy

A timeline is a top-level item whose panel holds rows; a row is an Icon + Label, and a row that expands is itself a nested Steps.Item:

<Steps.Root>
  <Steps.Item defaultOpen>
    <Steps.Trigger>
      <span>Worked for 3 seconds</span>
    </Steps.Trigger>
    <Steps.Panel>
      {/* a static, complete row */}
      <div>
        <Steps.Icon>{checkIcon}</Steps.Icon>
        <Steps.Label>Read the request</Steps.Label>
      </div>

      {/* a nested, expandable row */}
      <Steps.Item defaultOpen>
        <Steps.Trigger>
          <Steps.Icon>{checkIcon}</Steps.Icon>
          <Steps.Label>Searched the web</Steps.Label>
        </Steps.Trigger>
        <Steps.Panel>Found three relevant sources and skimmed each.</Steps.Panel>
      </Steps.Item>

      {/* an in-progress row — status overrides icon + label styling */}
      <div>
        <Steps.Icon status="active">{spinnerIcon}</Steps.Icon>
        <Steps.Label status="active">Writing the answer</Steps.Label>
      </div>
    </Steps.Panel>
  </Steps.Item>
</Steps.Root>

Status

status is an opaque string — you own the set (commonly "complete", "active", "pending", but add "error"/"skipped"/anything). Steps.Item takes a status and publishes it through context; Steps.Icon and Steps.Label inherit it, or override with their own status. Resolution is own prop → inherited → "complete". Every status-aware part reflects it as data-status for styling.

Keyboard

Each item is a standard disclosure: Steps.Trigger is a real button, so Tab moves between triggers and Enter/Space toggle the nearest item. Nested items nest their own triggers — there is no roving focus or composite widget; the tree is plain sequential tab order.

Accessibility

Disclosure semantics are inherited from the underlying Collapsible: aria-expanded/aria-controls on the trigger, an id-linked, hidden-managed panel. The item whose status is "active" additionally carries aria-current="step" — the same "active" convention defaultOpen already keys off.

Status itself is invisible to assistive tech by default: Steps.Icon is aria-hidden and color never announces. Mount Steps.Status inside rows whose status matters — it renders a visually-hidden span speaking the resolved status string, and takes children for localized copy:

<Steps.Trigger>
  <Steps.Icon />
  <Steps.Label>Searching the web</Steps.Label>
  <Steps.Status />
</Steps.Trigger>

API reference

Every part accepts className, style, and render (see Styling) and emits a bespoke part attribute (data-<part>) unless noted.

Steps

The timeline root. Renders data-steps. No part-specific props.

Steps.Item

One node of the tree (a disclosure). Renders data-steps-item, plus aria-current="step" while status is "active".

PropTypeDefault
statusstring
"complete"
defaultOpenboolean
status === active
openboolean
onOpenChange(open: boolean) => void
AttributeValuesDescription
data-steps-itemThe item element.
data-statusstringThe item's status (commonly complete / active / pending).
data-nested"true"Present when the item is inside another item (indent rail).
data-openPresent while open.
data-closedPresent while closed.

Steps.Trigger

Toggles the nearest item. Renders a <button data-steps-trigger> (aria-expanded, aria-controls). Carries data-open/data-closed for the chevron. The styled layer groups it as group/steps-trigger so children read group-data-open/steps-trigger:….

Steps.Panel

The nearest item's disclosure area — lays out the timeline column. Renders data-steps-panel.

PropTypeDefault
keepMountedboolean
false
AttributeDescription
data-steps-panelThe panel.
data-openPresent while open.
data-closedPresent while closed.
data-starting-stylePresent on the first open frame (enter transition).
data-ending-stylePresent while the exit animation runs.

Steps.Icon

Status indicator. Renders <span data-steps-icon aria-hidden>.

PropTypeDefault
statusstring
AttributeValuesDescription
data-steps-iconThe icon element.
data-statusstringResolved status, for styling.

Steps.Label

Row text. Renders <span data-steps-label>.

PropTypeDefault
statusstring
AttributeValuesDescription
data-steps-labelThe label element.
data-statusstringResolved status, for styling.

Steps.Status

Visually-hidden status announcement. Renders <span data-steps-status> with screen-reader-only styling (overridable via style/className), containing the resolved status string unless children provide localized copy.

PropTypeDefault
statusstring
childrenReactNode
the resolved status string
AttributeDescription
data-steps-statusThe status element.