Timeline

Events in order on a line, to show the history of a record, the steps of an order or the replies on a ticket.

  1. Order placed
    Paid by card, 1,840.00 USD.
  2. Packed
  3. Current step
    Out for delivery
    The courier is 6 stops away.
  4. Upcoming
    Delivered
import { Package, ShoppingCart, Truck } from "lucide-react"

import {

When to use it

Use a timeline to show what happened, or what will happen, in order: the steps of an order, the activity on a ticket, the history of a customer, the stages of a document. The line between the events tells people that they belong together and which comes first.

A timeline only shows. It does not take part: your app gives each event its state and its content. For steps that people move through and answer, build the form with Tabs or a wizard, and use a horizontal timeline as the progress bar on top of it.

Import

import {
  Timeline,
  TimelineItem,
  TimelineMarker,
  TimelineContent,
  TimelineHeader,
  TimelineTitle,
  TimelineTime,
  TimelineDescription,
} from "@hel/ui/timeline"

Anatomy

A Timeline is an ordered list of TimelineItems. Each item has a TimelineMarker, which is the circle on the line, and TimelineContent beside it. The content holds a TimelineHeader with a TimelineTitle and a TimelineTime, then a TimelineDescription, or the other parts below.

<Timeline aria-label="Order #10482">
  <TimelineItem tone="success">
    <TimelineMarker icon={ShoppingCart} />
    <TimelineContent>
      <TimelineHeader>
        <TimelineTitle>Order placed</TimelineTitle>
        <TimelineTime value={order.createdAt} />
      </TimelineHeader>
      <TimelineDescription>Paid by card.</TimelineDescription>
    </TimelineContent>
  </TimelineItem>
</Timeline>

Give the timeline an aria-label, so it has a name. The line between events is drawn by the layout alone, from the state of the neighbours, so nothing is measured and you can add or remove items freely.

TimelineTime is a relative time such as "2 hours ago", and a date when you give it a format. Both are exact in the tooltip.

States

state on an item says where it stands, and the marker, the lines and the text follow it.

StateMeaningLook
doneIt happened. This is the default.A marker with its icon or a dot, joined by a solid line
currentThis is where things are now.A ring around the marker
upcomingIt has not happened yet.A dashed, hollow marker, with a dashed line before it
pendingIt waits for someone or something.A spinner
failedIt went wrong.An error icon, in the error color

The state is also read to screen readers in words ("Current step", "Upcoming"), so it is not told by color and icon alone. A done event reads nothing extra by default. tone colors the marker: neutral, info, success, warning, error or primary.

Examples

Activity

A feed of what people did on a record. The marker can show a person with initials and src, and TimelineChanges lists the fields that changed, with the old value and the new one. Leave out from for a value that was set, and to for one that was cleared. TimelineDetails hides a longer text behind a button, and TimelineActions and TimelineMeta hold buttons and small print. An event that the system made, not a person, can be quiet, so it is muted, and a marker with no icon is a small dot.

  1. LF
    Lena Fischer replied
    I refunded the order. The money should be back in 3 to 5 days.
  2. OH
    Omar Haddad changed the ticket
    StatusOpenWaiting for refundPriorityNormalHighAssigneeLena Fischer

    From the support queue

  3. The customer was emailed
  4. MT
    Maya Torres opened the ticket
    The parcel arrived damaged.
import { Button } from "@hel/ui/button"
import {
  Timeline,

Layouts and states

orientation="horizontal" makes a strip of steps, with the marker above the title. Use it for four or five short steps, because there is no room for more. In the default vertical timeline, align puts the content start or end of the line, or alternate on both sides in turn. size is sm, md or lg.

Horizontal, for a short strip of steps

  1. Cart
  2. Address
  3. Current step
    Payment
  4. Upcoming
    Confirm

Pending, failed and upcoming events

  1. Uploaded
  2. Pending
    Scanning for viruses
  3. Failed
    Converted to PDF
  4. Upcoming
    Published
import {
  Timeline,
  TimelineContent,

Groups, and loading more

TimelineGroup puts events under a heading, such as a day or a month. The heading stays at the top while the events of its group scroll under it. Set surface="raised" on the timeline if it sits in a Card, so the heading matches the color behind it.

TimelineLoadMore is a button that belongs inside the timeline, after the last event. direction="newer" makes it for the start of the list, and loading shows a spinner on it while you fetch. While the very first events load, show a TimelineSkeleton in place of the whole timeline. A feed that updates while it is open can set live, so new events are announced politely.

  1. Today

    1. Invoice INV-2044 was paid
    2. Customer Harbor Studio was added
  2. Yesterday

    1. Invoice INV-2043 became overdue
    2. Report for September was exported
import * as React from "react"
import { Stack } from "@hel/ui/stack"
import {

Props

Timeline

PropTypeDefault
size"sm" | "md" | "lg""md"
orientation"vertical" | "horizontal""vertical"
align"start" | "end" | "alternate""start"
surface"page" | "raised""page"
livebooleanfalse

TimelineItem

PropTypeDefault
state"done" | "current" | "upcoming" | "pending" | "failed""done"
tone"neutral" | "info" | "success" | "warning" | "error" | "primary""neutral", or "error" when failed
quietbooleanfalse

TimelineMarker

PropTypeDefault
iconLucideIconnone
initials / src / altstringnone
childrenReactNodenone

Other parts

TimelineGroup takes heading. TimelineChanges takes changes, a list of { field, from, to }. TimelineDetails takes label, open and defaultOpen. TimelineSkeleton takes rows, and the props of a timeline. TimelineLoadMore takes direction, and the props of a Button.

The built-in texts, such as the state words read to screen readers and the labels of the buttons, can be changed with messages on the timeline, or translated for the whole app in the config.