|Kit

Timeline

Vertical, chronological list of events — activity feed, audit log, changelog. Pure Server Component with namespaced parts.

Timeline renders a vertical, chronological list of events. It's a pure Server Component — no state, no "use client" — so it drops straight into an RSC tree or an MDX page.

You compose it from Timeline.Item (one per event — it renders the bullet and the connecting line to the next item automatically), with two optional children: Timeline.Label for a leading date/timestamp, and Timeline.Content for the body, which can hold a Timeline.Title.

import { Timeline } from "@42-central/ui-react/timeline";

<Timeline>
  <Timeline.Item color="green" variant="filled">
    <Timeline.Label>Mar 1, 2026</Timeline.Label>
    <Timeline.Content>
      <Timeline.Title>Libft submitted</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="outline">
    <Timeline.Content>
      <Timeline.Title>Evaluation validated</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>;

Basic

Without Timeline.Label, items lay out in a single column — the label track only exists when at least one item uses it (see Labels). An item with no variant gets Timeline's own default — the same neutral, high-contrast (variant × color) default treatment as Button/Card, not a colored dot.

  1. Application submitted
  2. Logic test passed
  3. Piscine started

Code

<Timeline>
  <Timeline.Item color="brand" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Application submitted</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Logic test passed</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>Piscine started</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Labels

Timeline.Label renders into a leading column, sized to fit whatever the widest label is — not a fixed split. Add it to just one item and the column still appears (sized to that one label) without disturbing the others' alignment. Pass color to tint the label (and any icon inside it) to match the item's accent, and compose an icon in directly — Timeline.Label lays its children out in a row.

  1. Mar 1, 2026

    Libft submitted
  2. Mar 3, 2026

    Evaluation booked
  3. Pending

    ft_printf

Code

<Timeline>
  <Timeline.Item color="green" variant="filled">
    <Timeline.Label color="green"><Calendar />Mar 1, 2026</Timeline.Label>
    <Timeline.Content>
      <Timeline.Title>Libft submitted</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="green" variant="filled">
    <Timeline.Label color="green"><Calendar />Mar 3, 2026</Timeline.Label>
    <Timeline.Content>
      <Timeline.Title>Evaluation booked</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Label><Calendar />Pending</Timeline.Label>
    <Timeline.Content>
      <Timeline.Title>ft_printf</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Status colors & custom bullets

color and variant on Timeline.Item theme the bullet — the same (variant × color) axis as Badge/Button. filled rings its own fill with a background-colored gap, so it reads as a halo rather than a flat disc. Pass a bullet node (an icon, <Spinner>, anything) to replace the dot entirely, e.g. for an in-progress step.

  1. Libft validated
  2. ft_printf in evaluation
  3. Loading
    get_next_line in progress
  4. Minishell

Code

<Timeline>
  <Timeline.Item color="green" variant="filled" bullet={<Check />}>
    <Timeline.Content>
      <Timeline.Title>Libft validated</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="filled" bullet={<Users />}>
    <Timeline.Content>
      <Timeline.Title>ft_printf in evaluation</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="filled" bullet={<Spinner />}>
    <Timeline.Content>
      <Timeline.Title>get_next_line in progress</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="outline">
    <Timeline.Content>
      <Timeline.Title>Minishell</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Line colors

Every connecting line blends from its dot's color into the next dot's, so a run of statuses reads as one continuous path instead of hard color cuts at each bullet. Every line end is always the exact color its dot draws — whatever the variant, color, or state below — so the line never lands on a color the dot doesn't have.

Blending between colors

Any two neighbours blend — palette colors, a gradient, and a default dot, in whatever order.

  1. Libft validated 125/100 — bonus included.

  2. ft_printf retry Failed the first evaluation, booked a second slot.

  3. Blackhole in 12 days Validate a project to push it back.

  4. Common Core reached Gradient bullets blend through their own stops.

  5. Specialization

Code

<Timeline size="lg">
  <Timeline.Item color="green" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Libft validated</Timeline.Title>
      125/100 — bonus included.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="yellow" variant="filled">
    <Timeline.Content>
      <Timeline.Title>ft_printf retry</Timeline.Title>
      Failed the first evaluation, booked a second slot.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="red" variant="outline">
    <Timeline.Content>
      <Timeline.Title>Blackhole in 12 days</Timeline.Title>
      Validate a project to push it back.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="gradient">
    <Timeline.Content>
      <Timeline.Title>Common Core reached</Timeline.Title>
      Gradient bullets blend through their own stops.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>Specialization</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Every variant

Each end takes the color its bullet draws with: --c-solid for filled, outline and dashed, a gradient's own stops, and a default dot's inverted fill.

  1. filled Ends in --c-solid, its fill.

  2. outline Ends in --c-solid, its ring.

  3. dashed Ends in --c-solid, its dashed ring.

  4. gradient Entered at from, left at to.

  5. default Ends in its own inverted fill.

Code

<Timeline size="lg">
  <Timeline.Item color="brand" variant="filled">
    <Timeline.Content>
      <Timeline.Title>filled</Timeline.Title>
      Ends in --c-solid, its fill.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="outline">
    <Timeline.Content>
      <Timeline.Title>outline</Timeline.Title>
      Ends in --c-solid, its ring.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="dashed">
    <Timeline.Content>
      <Timeline.Title>dashed</Timeline.Title>
      Ends in --c-solid, its dashed ring.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="gradient">
    <Timeline.Content>
      <Timeline.Title>gradient</Timeline.Title>
      Entered at from, left at to.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>default</Timeline.Title>
      Ends in its own inverted fill.
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Uncolored bullets

Without a color, a bullet draws in the neutral palette — and its line ends read the same slots, so they land on the neutral too.

  1. filled
  2. outline
  3. dashed

Code

<Timeline size="lg">
  <Timeline.Item variant="filled">
    <Timeline.Content>
      <Timeline.Title>filled</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="outline">
    <Timeline.Content>
      <Timeline.Title>outline</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="dashed">
    <Timeline.Content>
      <Timeline.Title>dashed</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Gradient stops

A gradient bullet is entered at its from stop and left at its to, so chained gradients read as one continuous fade — each line runs from one bullet's to into the next one's from.

  1. Sunrise Amber → red.

  2. Ocean The line runs red → cyan.

  3. Default stops The line runs indigo → purple.

  4. Pink Picks up from the pink stop.

Code

<Timeline size="lg">
  <Timeline.Item variant="gradient" gradient={{ from: "#f59e0b", to: "#ef4444" }}>
    <Timeline.Content>
      <Timeline.Title>Sunrise</Timeline.Title>
      Amber → red.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="gradient" gradient={{ from: "#22d3ee", to: "#6366f1" }}>
    <Timeline.Content>
      <Timeline.Title>Ocean</Timeline.Title>
      The line runs red → cyan.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="gradient">
    <Timeline.Content>
      <Timeline.Title>Default stops</Timeline.Title>
      The line runs indigo → purple.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="pink" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Pink</Timeline.Title>
      Picks up from the pink stop.
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Dashes and icons

Dashes are cut out of the line with a mask, so a dashed segment still blends. A custom bullet replaces only the dot's content — the line still follows the item's color, variant and state.

  1. libft Solid, green → brand.

  2. ft_printf Dashed below, brand → brand.

  3. Loading

    get_next_line Dashed below, brand → the active dot.

  4. minishell Active.

  5. philosophers Pending.

Code

<Timeline size="lg" active={3}>
  <Timeline.Item color="green" variant="filled" bullet={<Check />}>
    <Timeline.Content>
      <Timeline.Title>libft</Timeline.Title>
      Solid, green → brand.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="filled" bullet={<Users />} lineVariant="dashed">
    <Timeline.Content>
      <Timeline.Title>ft_printf</Timeline.Title>
      Dashed below, brand → brand.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="filled" bullet={<Spinner />} lineVariant="dashed">
    <Timeline.Content>
      <Timeline.Title>get_next_line</Timeline.Title>
      Dashed below, brand → the active dot.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>minishell</Timeline.Title>
      Active.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>philosophers</Timeline.Title>
      Pending.
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Progress

Pass active — the index of the current step among the Timeline.Items — to turn a timeline into progress. Steps up to and including it render as usual, and the active one gets aria-current="step". Steps after it are pending: every variant switches to the palette's disabled colors (colored ones included), a default dot shrinks to its bare outer ring, and a gradient flattens. Each item exposes its state as data-state (complete, active or pending) for your own styling.

  1. libftComplete.
  2. ft_printfComplete.
  3. get_next_lineActive — aria-current="step".
  4. minitalkPending — a bare ring.
  5. pipexPending — colored items dim too.
  6. philosophersPending — the gradient flattens.

Code

<Timeline size="lg" active={2}>
  <Timeline.Item color="green" variant="filled">
    <Timeline.Content>
      <Timeline.Title>libft</Timeline.Title>
      Complete.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>ft_printf</Timeline.Title>
      Complete.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>get_next_line</Timeline.Title>
      Active — aria-current="step".
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>minitalk</Timeline.Title>
      Pending — a bare ring.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="red" variant="filled">
    <Timeline.Content>
      <Timeline.Title>pipex</Timeline.Title>
      Pending — colored items dim too.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="gradient">
    <Timeline.Content>
      <Timeline.Title>philosophers</Timeline.Title>
      Pending — the gradient flattens.
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Marking the active item

When the data already knows which entry is current, pass active on that Timeline.Item instead of computing an index. An item's active overrides the root's; if several items pass it, the first one wins, and active={false} is the same as leaving it unset.

  1. Application
  2. Piscine
  3. Common Core Marked on the item — overrides the root.

  4. Specialization

Code

<Timeline size="lg" active={0}>
  <Timeline.Item color="brand" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Application</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="brand" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Piscine</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item active>
    <Timeline.Content>
      <Timeline.Title>Common Core</Timeline.Title>
      Marked on the item — overrides the root.
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>Specialization</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Out of range

An index past the last step marks every step complete; a negative one marks every step pending. Leave active unset on both levels for a plain event list — nothing dims, and the line between default dots is their own fill.

  1. Joined the campus
  2. Met the cohort
  3. First project

Code

<Timeline size="lg" active={-1}>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>Joined the campus</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>Met the cohort</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>First project</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

The short stretch axisOffset opens above a shifted bullet is drawn in that bullet's own entry color — see Gradient with axisOffset.

Dashed bullet

variant="dashed" draws the outline ring with a dashed stroke instead of solid — reads as a lighter-weight or "not yet reached" step, distinct from outline's solid ring.

  1. Evaluated
  2. Evaluation scheduled
  3. Waiting for a slot

Code

<Timeline>
  <Timeline.Item color="purple" variant="filled">
    <Timeline.Content>
      <Timeline.Title>Evaluated</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="purple" variant="dashed">
    <Timeline.Content>
      <Timeline.Title>Evaluation scheduled</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item color="purple" variant="outline">
    <Timeline.Content>
      <Timeline.Title>Waiting for a slot</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Gradient

variant="gradient" fills the bullet with a gradient instead of a flat color, the same gradient prop shape as Badge/Card (from/to/via/dir, defaulting to purple-300 → pink-400, left-to-right).

  1. Bocal member
  2. Blackhole warning

Code

<Timeline.Item variant="gradient">{/* … */}</Timeline.Item>
<Timeline.Item variant="gradient" gradient={{ from: '#f59e0b', to: '#ef4444', dir: 'to-br' }}>
  {/* … */}
</Timeline.Item>

Gradient with axisOffset

axisOffset works the same regardless of variant — it only moves the bullet+line column, never the color. The connector above the shifted bullet still blends all the way into it, and the short stretch axisOffset opens above the bullet is drawn in that bullet's own entry color — for a gradient, its from stop — so the line meets it with no seam.

  1. ft_transcendence

    In progress — gradient bullet aligned to this title via axisOffset.

  2. Common Core completed

Code

<Timeline size="lg">
  <Timeline.Item size="xl" variant="gradient" axisOffset="11px">
    <Timeline.Content>
      <Card padding="none">
        <Card.Header>
          <Card.Title>ft_transcendence</Card.Title>
        </Card.Header>
        <Card.Content>
          <Text span c="muted">
            In progress — gradient bullet aligned to this title via axisOffset.
          </Text>
        </Card.Content>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item variant="outline" size="xs" axisOffset="18px">
    <Timeline.Content>
      <Card padding="none">
        <Card.Header>
          <Card.Title>Common Core completed</Card.Title>
        </Card.Header>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Dashed line

lineVariant="dashed" on Timeline swaps every connecting line for a dashed one. A Timeline.Item can override just the segment below it with its own lineVariant, mixing solid and dashed within one timeline. Dashes are cut out of the line with a mask, so a dashed segment still blends between its two items' colors.

  1. libft pushed
  2. Awaiting evaluation
  1. ft_printf validated
  2. minitalk in evaluation
  3. pipex

Code

<Timeline lineVariant="dashed">{/* … */}</Timeline>

// Mixed, per segment:

<Timeline>
  <Timeline.Item color="brand" variant="filled">{/* solid below */}</Timeline.Item>
  <Timeline.Item lineVariant="dashed">{/* dashed below */}</Timeline.Item>
  <Timeline.Item variant="outline">{/* … */}</Timeline.Item>
</Timeline>

Density

size on Timeline scales the bullet, line thickness, spacing, and text from xs to xl (default md), reusing the kit's canonical Size scale. A Timeline.Item can override just its own bullet with its own size — everything else (line, spacing, text) still follows the timeline's density.

  1. Compact (sm)
  2. Common Core project
  1. Roomy (lg)
  2. Common Core project

Code

<Timeline size="sm">{/* … */}</Timeline>
<Timeline size="lg">{/* … */}</Timeline>
  1. Piscine started — bigger dot, same line and spacing

  2. Common Core project

Code

<Timeline>
  <Timeline.Item color="brand" variant="filled" size="xl">
    <Timeline.Content>
      <Timeline.Title>Piscine started — bigger dot, same line and spacing</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item>
    <Timeline.Content>
      <Timeline.Title>Common Core project</Timeline.Title>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Course roadmap

Because Timeline.Content takes any content, an item's body is often a full card, not just a title — a curriculum roadmap composes Card (+ Badge for status, Button for actions) inside each item. active={0} marks the first card as the current step: it keeps a larger, ringed default bullet and a brighter card border, while the upcoming steps render as small pending rings next to the card's own quieter default border. Each item's axisOffset="Npx" nudges the bullet and the connecting line down together to line up with that card's title — Timeline has no way to know a title sits inside its content on its own.

  1. Shell Fundamentals

    Available

    Learn the basics of using the shell, a command-line interpreter that allows you to interact with your operating system.

  2. Git Fundamentals

    Unavailable

    Learn the basics of Git, a popular version control system used to track changes in your code, collaborate with others, and submit your work for evaluation.

  3. Shell searching and finding

    Unavailable

    Learn how to find files and information within your system thanks to find and grep to locate specific files, search within files, and filter results based on various criteria.

  4. Exam 00

    Exam details will soon be available.

  5. Square

Code

<Timeline size="lg" active={0}>
  {/* axisOffset shifts the bullet+line column down to line up with this
      card's title — Timeline can't see inside Timeline.Content to know
      where a title sits on its own. */}
  <Timeline.Item size="xl" axisOffset="11px">
    <Timeline.Content>
      <Card padding="none" className="border-gray-light-400 dark:border-gray-dark-400">
        <Card.Header>
          <div className="flex items-center justify-between gap-3">
            <Card.Title>Shell Fundamentals</Card.Title>
            <Badge variant="light">Available</Badge>
          </div>
        </Card.Header>
        <Card.Content>
          <Text span c="muted">
            Learn the basics of using the shell, a command-line interpreter
            that allows you to interact with your operating system.
          </Text>
        </Card.Content>
        <Card.Footer>
          <Button size="sm">Discover my project</Button>
        </Card.Footer>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item size="xs" axisOffset="18px">
    <Timeline.Content>
      <Card padding="none">
        <Card.Header>
          <div className="flex items-center justify-between gap-3">
            <Card.Title>Git Fundamentals</Card.Title>
            <Badge variant="light" color="neutral">Unavailable</Badge>
          </div>
        </Card.Header>
        <Card.Content>
          <Text span c="muted">
            Learn the basics of Git, a popular version control system used
            to track changes in your code, collaborate with others, and
            submit your work for evaluation.
          </Text>
        </Card.Content>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item size="xs" axisOffset="18px">
    <Timeline.Content>
      <Card padding="none">
        <Card.Header>
          <div className="flex items-center justify-between gap-3">
            <Card.Title>Shell searching and finding</Card.Title>
            <Badge variant="light" color="neutral">Unavailable</Badge>
          </div>
        </Card.Header>
        <Card.Content>
          <Text span c="muted">
            Learn how to find files and information within your system
            thanks to find and grep to locate specific files, search within
            files, and filter results based on various criteria.
          </Text>
        </Card.Content>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item size="xs" axisOffset="18px">
    <Timeline.Content>
      <Card padding="none">
        <Card.Header>
          <Card.Title>Exam 00</Card.Title>
        </Card.Header>
        <Card.Content>
          <Text span c="muted">Exam details will soon be available.</Text>
        </Card.Content>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
  <Timeline.Item size="xs" axisOffset="22px">
    <Timeline.Content>
      <Card padding="none">
        <Card.Header>
          <div className="flex items-center justify-between gap-3">
            <Card.Title>Square</Card.Title>
            <div className="flex gap-2">
              <Button size="sm">Register</Button>
              <Button variant="subtle" color="white" size="sm">Go to Rush</Button>
            </div>
          </div>
        </Card.Header>
      </Card>
    </Timeline.Content>
  </Timeline.Item>
</Timeline>

Subcomponents

Prop

Type

Every part renders testId as data-testid on its own DOM node — see Testing.

API

Timeline

Prop

Type

Timeline.Item

Prop

Type

Changelog

v0.19.0
  • Bullet rings and the gradient bullet's cut-out paint the page background in light mode instead of white

    #316
v0.18.0
  • Pill, Timeline.Item, Alert and ScheduleToolbar.Title render their content inside your element under asChild instead of rendering nothing

    #288
v0.17.0
  • Remove the subtle and light item variants

    #268
  • active marks the current step, and each line blends from its dot's color into the next

    #268
v0.13.0
  • Reconcile the card border and withBorder dividers to one hairline

    #193
v0.3.0
  • New Timeline: a vertical event list with dashed and gradient line variants and per-item color

    #22

On this page