ComponentsTimeline
Timeline
Show events in order with statuses and optional selection.
On this page
Install component source from the public registry, or use the local workspace package. Follow the setup guide first. The npm package is not published yet.
View registry files- Lucide
(lucide-react)React SVG icons used in component controls and status indicators.
Shared foundation: React, clsx for conditional classes, tailwind-merge for resolving utility-class conflicts. Styles use Tailwind CSS and Dethink CSS variables.
Based on this component’s source and registry composition. Libraries used only in showcase examples are excluded.
Import the component into your page or component file.
import {
Timeline,
type TimelineItemData,
} from "@dethink/components";Try the examples, then open the code to use them in your app. Interactive timelines support arrow keys and Home/End. Use flow for readable histories and canvas when people need to explore a large track.
Activity history
Dated milestones with complete, current, and upcoming statuses.
- Status: Complete
Research complete
User needs and interaction model approved.
- Status: Current
Private beta
First teams using the component in dashboards.
- Status: Upcoming
General availability
Registry item, docs, and release notes ship.
Show sourceexamples/timeline/basic.tsx
"use client";
import { Timeline, type TimelineItemData } from "@dethink/components";
const milestones: TimelineItemData[] = [
{
id: "research",
title: "Research complete",
description: "User needs and interaction model approved.",
datetime: "2026-01-12T09:00:00Z",
dateLabel: "Jan 12",
status: "complete",
},
{
id: "beta",
title: "Private beta",
description: "First teams using the component in dashboards.",
datetime: "2026-03-18T10:00:00Z",
dateLabel: "Mar 18",
status: "current",
},
{
id: "ga",
title: "General availability",
description: "Registry item, docs, and release notes ship.",
datetime: "2026-05-05T10:00:00Z",
dateLabel: "May 5",
status: "upcoming",
},
];
export function TimelineBasic() {
return (
<Timeline
interactive={false}
aria-label="Release milestones"
items={milestones}
/>
);
}Deployment progress
A readable sequence of completed, current and upcoming stages.
- Status: Complete
Queued
Waiting for a build slot.
- Status: Current
Building
Compiling and running checks.
- Status: Upcoming
Deploying
Rolling out to the edge.
- Status: Upcoming
Live
Serving production traffic.
Show sourceexamples/timeline/progress.tsx
"use client";
import { Timeline, type TimelineItemData } from "@dethink/components";
const steps: TimelineItemData[] = [
{
id: "queued",
title: "Queued",
description: "Waiting for a build slot.",
status: "complete",
},
{
id: "building",
title: "Building",
description: "Compiling and running checks.",
status: "current",
},
{
id: "deploying",
title: "Deploying",
description: "Rolling out to the edge.",
status: "upcoming",
},
{
id: "live",
title: "Live",
description: "Serving production traffic.",
status: "upcoming",
},
];
export function TimelineProgress() {
return (
<Timeline
interactive={false}
aria-label="Deployment progress"
mode="progress"
scale="auto"
layout="stacked"
items={steps}
/>
);
}Grouped release activity
Scan by date, then expand an event for its owner and commit. Details do not change selection.
- September 21
- Status: CompleteSep 21 · 16:20
Release approved
Review complete. Production rollout scheduled.
- September 22
- Status: CompleteSep 22 · 09:02
Build verified
All checks passed across the release targets.
- Status: WarningSep 22 · 09:06
Canary needs attention
Latency is above the rollout threshold.
- Status: CurrentSep 22 · 09:12
Production rollout
Monitoring the first production region.
Show sourceexamples/timeline/enhanced.tsx
"use client";
import { useState } from "react";
import {
Button,
Timeline,
TimelineFeed,
type TimelineItemData,
} from "@dethink/components";
type RunData = { day: string; author: string; commit: string };
const history: TimelineItemData<RunData>[] = [
{
id: "plan",
title: "Release approved",
dateLabel: "Sep 21 · 16:20",
status: "complete",
data: { day: "September 21", author: "Dana", commit: "a92b641" },
description: "Review complete. Production rollout scheduled.",
},
{
id: "build",
title: "Build verified",
dateLabel: "Sep 22 · 09:02",
status: "complete",
data: { day: "September 22", author: "Miguel", commit: "f642257" },
description: "All checks passed across the release targets.",
},
{
id: "canary",
title: "Canary needs attention",
dateLabel: "Sep 22 · 09:06",
status: "warning",
data: { day: "September 22", author: "Priya", commit: "f642257" },
description: "Latency is above the rollout threshold.",
},
{
id: "rollout",
title: "Production rollout",
dateLabel: "Sep 22 · 09:12",
status: "current",
data: { day: "September 22", author: "Dana", commit: "f642257" },
description: "Monitoring the first production region.",
},
];
export function TimelineGroupedDetails() {
return (
<Timeline
aria-label="Grouped release activity"
items={history}
interactive={false}
getGroup={(item) =>
item.data ? { id: item.data.day, label: item.data.day } : null
}
renderDetails={(item) => (
<div className="space-y-2">
<p>Owner: {item.data?.author}</p>
<p>
Commit: <code>{item.data?.commit}</code>
</p>
<Button
variant="outline"
size="sm"
onClick={() => {
void navigator.clipboard.writeText(item.data?.commit ?? "");
}}
>
Copy commit
</Button>
</div>
)}
/>
);
}
const initialFeed: TimelineItemData[] = Array.from(
{ length: 8 },
(_, index) => ({
id: `event-${index}`,
title: `Check ${index + 1} completed`,
description: "Release checks recorded and ready for review.",
dateLabel: `09:${String(index).padStart(2, "0")}`,
status: "complete",
}),
);
export function TimelineLiveFeed() {
const [items, setItems] = useState(initialFeed);
return (
<div className="w-full space-y-4">
<div className="flex flex-wrap gap-2">
<Button
size="sm"
onClick={() =>
setItems((current) => [
...current,
{
id: `event-${current.length}`,
title: `Check ${current.length + 1} completed`,
description: "A new result arrived from the release agent.",
dateLabel: "Just now",
status: "complete",
},
])
}
>
Add event
</Button>
<Button
size="sm"
variant="outline"
onClick={() =>
setItems((current) => [
{
id: `older-${current.length}`,
title: "Earlier release note",
description:
"An older event loaded without adding to the unread count.",
dateLabel: "Earlier",
status: "neutral",
},
...current,
])
}
>
Load earlier example
</Button>
</div>
<TimelineFeed
aria-label="Live release activity"
items={items}
interactive={false}
reveal="stagger"
/>
</div>
);
}
export function TimelineCards() {
return (
<Timeline
aria-label="Release cards"
items={history.slice(1, 3)}
variant="cards"
interactive={false}
/>
);
}Live release feed
Scroll up, then add an event. Your reading position stays put until you choose Jump to latest.
- Status: Complete09:00
Check 1 completed
Release checks recorded and ready for review.
- Status: Complete09:01
Check 2 completed
Release checks recorded and ready for review.
- Status: Complete09:02
Check 3 completed
Release checks recorded and ready for review.
- Status: Complete09:03
Check 4 completed
Release checks recorded and ready for review.
- Status: Complete09:04
Check 5 completed
Release checks recorded and ready for review.
- Status: Complete09:05
Check 6 completed
Release checks recorded and ready for review.
- Status: Complete09:06
Check 7 completed
Release checks recorded and ready for review.
- Status: Complete09:07
Check 8 completed
Release checks recorded and ready for review.
Show sourceexamples/timeline/enhanced.tsx
"use client";
import { useState } from "react";
import {
Button,
Timeline,
TimelineFeed,
type TimelineItemData,
} from "@dethink/components";
type RunData = { day: string; author: string; commit: string };
const history: TimelineItemData<RunData>[] = [
{
id: "plan",
title: "Release approved",
dateLabel: "Sep 21 · 16:20",
status: "complete",
data: { day: "September 21", author: "Dana", commit: "a92b641" },
description: "Review complete. Production rollout scheduled.",
},
{
id: "build",
title: "Build verified",
dateLabel: "Sep 22 · 09:02",
status: "complete",
data: { day: "September 22", author: "Miguel", commit: "f642257" },
description: "All checks passed across the release targets.",
},
{
id: "canary",
title: "Canary needs attention",
dateLabel: "Sep 22 · 09:06",
status: "warning",
data: { day: "September 22", author: "Priya", commit: "f642257" },
description: "Latency is above the rollout threshold.",
},
{
id: "rollout",
title: "Production rollout",
dateLabel: "Sep 22 · 09:12",
status: "current",
data: { day: "September 22", author: "Dana", commit: "f642257" },
description: "Monitoring the first production region.",
},
];
export function TimelineGroupedDetails() {
return (
<Timeline
aria-label="Grouped release activity"
items={history}
interactive={false}
getGroup={(item) =>
item.data ? { id: item.data.day, label: item.data.day } : null
}
renderDetails={(item) => (
<div className="space-y-2">
<p>Owner: {item.data?.author}</p>
<p>
Commit: <code>{item.data?.commit}</code>
</p>
<Button
variant="outline"
size="sm"
onClick={() => {
void navigator.clipboard.writeText(item.data?.commit ?? "");
}}
>
Copy commit
</Button>
</div>
)}
/>
);
}
const initialFeed: TimelineItemData[] = Array.from(
{ length: 8 },
(_, index) => ({
id: `event-${index}`,
title: `Check ${index + 1} completed`,
description: "Release checks recorded and ready for review.",
dateLabel: `09:${String(index).padStart(2, "0")}`,
status: "complete",
}),
);
export function TimelineLiveFeed() {
const [items, setItems] = useState(initialFeed);
return (
<div className="w-full space-y-4">
<div className="flex flex-wrap gap-2">
<Button
size="sm"
onClick={() =>
setItems((current) => [
...current,
{
id: `event-${current.length}`,
title: `Check ${current.length + 1} completed`,
description: "A new result arrived from the release agent.",
dateLabel: "Just now",
status: "complete",
},
])
}
>
Add event
</Button>
<Button
size="sm"
variant="outline"
onClick={() =>
setItems((current) => [
{
id: `older-${current.length}`,
title: "Earlier release note",
description:
"An older event loaded without adding to the unread count.",
dateLabel: "Earlier",
status: "neutral",
},
...current,
])
}
>
Load earlier example
</Button>
</div>
<TimelineFeed
aria-label="Live release activity"
items={items}
interactive={false}
reveal="stagger"
/>
</div>
);
}
export function TimelineCards() {
return (
<Timeline
aria-label="Release cards"
items={history.slice(1, 3)}
variant="cards"
interactive={false}
/>
);
}Bordered cards
Use cards when events benefit from more separation.
- Status: CompleteSep 22 · 09:02
Build verified
All checks passed across the release targets.
- Status: WarningSep 22 · 09:06
Canary needs attention
Latency is above the rollout threshold.
Show sourceexamples/timeline/enhanced.tsx
"use client";
import { useState } from "react";
import {
Button,
Timeline,
TimelineFeed,
type TimelineItemData,
} from "@dethink/components";
type RunData = { day: string; author: string; commit: string };
const history: TimelineItemData<RunData>[] = [
{
id: "plan",
title: "Release approved",
dateLabel: "Sep 21 · 16:20",
status: "complete",
data: { day: "September 21", author: "Dana", commit: "a92b641" },
description: "Review complete. Production rollout scheduled.",
},
{
id: "build",
title: "Build verified",
dateLabel: "Sep 22 · 09:02",
status: "complete",
data: { day: "September 22", author: "Miguel", commit: "f642257" },
description: "All checks passed across the release targets.",
},
{
id: "canary",
title: "Canary needs attention",
dateLabel: "Sep 22 · 09:06",
status: "warning",
data: { day: "September 22", author: "Priya", commit: "f642257" },
description: "Latency is above the rollout threshold.",
},
{
id: "rollout",
title: "Production rollout",
dateLabel: "Sep 22 · 09:12",
status: "current",
data: { day: "September 22", author: "Dana", commit: "f642257" },
description: "Monitoring the first production region.",
},
];
export function TimelineGroupedDetails() {
return (
<Timeline
aria-label="Grouped release activity"
items={history}
interactive={false}
getGroup={(item) =>
item.data ? { id: item.data.day, label: item.data.day } : null
}
renderDetails={(item) => (
<div className="space-y-2">
<p>Owner: {item.data?.author}</p>
<p>
Commit: <code>{item.data?.commit}</code>
</p>
<Button
variant="outline"
size="sm"
onClick={() => {
void navigator.clipboard.writeText(item.data?.commit ?? "");
}}
>
Copy commit
</Button>
</div>
)}
/>
);
}
const initialFeed: TimelineItemData[] = Array.from(
{ length: 8 },
(_, index) => ({
id: `event-${index}`,
title: `Check ${index + 1} completed`,
description: "Release checks recorded and ready for review.",
dateLabel: `09:${String(index).padStart(2, "0")}`,
status: "complete",
}),
);
export function TimelineLiveFeed() {
const [items, setItems] = useState(initialFeed);
return (
<div className="w-full space-y-4">
<div className="flex flex-wrap gap-2">
<Button
size="sm"
onClick={() =>
setItems((current) => [
...current,
{
id: `event-${current.length}`,
title: `Check ${current.length + 1} completed`,
description: "A new result arrived from the release agent.",
dateLabel: "Just now",
status: "complete",
},
])
}
>
Add event
</Button>
<Button
size="sm"
variant="outline"
onClick={() =>
setItems((current) => [
{
id: `older-${current.length}`,
title: "Earlier release note",
description:
"An older event loaded without adding to the unread count.",
dateLabel: "Earlier",
status: "neutral",
},
...current,
])
}
>
Load earlier example
</Button>
</div>
<TimelineFeed
aria-label="Live release activity"
items={items}
interactive={false}
reveal="stagger"
/>
</div>
);
}
export function TimelineCards() {
return (
<Timeline
aria-label="Release cards"
items={history.slice(1, 3)}
variant="cards"
interactive={false}
/>
);
}Horizontal timeline
Arrange milestones from left to right with presentation="canvas" and orientation="horizontal". Drag the track, use Fit to see the sequence, or focus Select and navigate with Left/Right and Home/End.
Drag the track · Arrow keys to navigate
- Status: CompleteSeptember 21
Planning complete
Scope agreed and the release scheduled.
- Status: CompleteSeptember 22
Build verified
Tests passed across all release targets.
- Status: CurrentSeptember 23
Production rollout
Monitoring the first production region.
- Status: UpcomingSeptember 24
Release review
Review reliability and customer feedback.
Show sourceexamples/timeline/horizontal.tsx
"use client";
import { Timeline, type TimelineItemData } from "@dethink/components";
const milestones: TimelineItemData[] = [
{
id: "planning",
title: "Planning complete",
dateLabel: "September 21",
description: "Scope agreed and the release scheduled.",
status: "complete",
},
{
id: "build",
title: "Build verified",
dateLabel: "September 22",
description: "Tests passed across all release targets.",
status: "complete",
},
{
id: "rollout",
title: "Production rollout",
dateLabel: "September 23",
description: "Monitoring the first production region.",
status: "current",
},
{
id: "review",
title: "Release review",
dateLabel: "September 24",
description: "Review reliability and customer feedback.",
status: "upcoming",
},
];
export function TimelineHorizontal() {
return (
<Timeline
aria-label="Horizontal release milestones"
items={milestones}
presentation="canvas"
orientation="horizontal"
layout="rail"
scale="sequence"
viewport={{ controlsVisibility: "always" }}
/>
);
}Flow presentation with reveal
Milestones enter as you reach them. Reduced-motion users see the content immediately.
- Status: Complete
Project kickoff
Scope agreed and delivery squad assembled.
- Status: Complete
Design review
Flows approved with two accessibility follow-ups.
- Status: Current
Build sprint
Core screens implemented behind a feature flag.
- Status: Upcoming
Launch
Flag removed and rollout announced.
Show sourceexamples/timeline/flow-reveal.tsx
"use client";
import { Timeline, type TimelineItemData } from "@dethink/components";
const milestones: TimelineItemData[] = [
{
id: "kickoff",
title: "Project kickoff",
description: "Scope agreed and delivery squad assembled.",
datetime: "2026-02-02T09:00:00Z",
dateLabel: "Feb 2",
status: "complete",
},
{
id: "design",
title: "Design review",
description: "Flows approved with two accessibility follow-ups.",
datetime: "2026-03-09T14:00:00Z",
dateLabel: "Mar 9",
status: "complete",
},
{
id: "build",
title: "Build sprint",
description: "Core screens implemented behind a feature flag.",
datetime: "2026-04-20T10:00:00Z",
dateLabel: "Apr 20",
status: "current",
},
{
id: "launch",
title: "Launch",
description: "Flag removed and rollout announced.",
datetime: "2026-06-01T10:00:00Z",
dateLabel: "Jun 1",
status: "upcoming",
},
];
/**
* presentation="flow" renders the same events data as a static document-flow
* list — no pan/zoom viewport — and reveal="stagger" animates the items in
* one by one as the timeline scrolls into view. Reduced-motion users see
* every item immediately.
*/
export function TimelineFlowReveal() {
return (
<Timeline
interactive={false}
aria-label="Project milestones"
items={milestones}
presentation="flow"
reveal="stagger"
revealOptions={{ trigger: "in-view" }}
/>
);
}Streaming reveal
Appending to the items array animates only the new points in — built for live sources like agent runs, deploy logs, or activity feeds.
- Status: Complete12:04:01
Run queued
Agent run accepted and scheduled.
- Status: Complete12:04:07
Plan drafted
Task broken into three tool calls.
Show sourceexamples/timeline/reveal-streaming.tsx
"use client";
import { useState } from "react";
import { Button, Timeline, type TimelineItemData } from "@dethink/components";
const runEvents: TimelineItemData[] = [
{
id: "queued",
title: "Run queued",
description: "Agent run accepted and scheduled.",
dateLabel: "12:04:01",
status: "complete",
},
{
id: "plan",
title: "Plan drafted",
description: "Task broken into three tool calls.",
dateLabel: "12:04:07",
status: "complete",
},
{
id: "search",
title: "Search executed",
description: "Indexed 42 documents, 6 strong matches.",
dateLabel: "12:04:18",
status: "complete",
},
{
id: "draft",
title: "Draft generated",
description: "Summary produced from the top matches.",
dateLabel: "12:04:31",
status: "current",
},
{
id: "review",
title: "Awaiting review",
description: "Draft handed off for human approval.",
dateLabel: "12:04:32",
status: "upcoming",
},
];
/**
* Growing the items array appends points live: only the new items animate in,
* so a streaming source — an agent run, a deploy log, a support thread — can
* push events onto the timeline one at a time.
*/
export function TimelineRevealStreaming() {
const [count, setCount] = useState(2);
const done = count >= runEvents.length;
return (
<div className="space-y-4">
<div className="flex justify-center gap-2">
<Button
size="sm"
disabled={done}
onClick={() => setCount((current) => current + 1)}
>
Log next event
</Button>
<Button size="sm" variant="outline" onClick={() => setCount(2)}>
Reset
</Button>
</div>
<Timeline
interactive={false}
aria-label="Agent run events"
items={runEvents.slice(0, count)}
presentation="flow"
reveal="stagger"
/>
</div>
);
}Examples that combine components for common tasks.
LLM story
A publication-style vertical history showing how LLMs moved from research architecture to everyday product workflows.
- Status: Complete
Attention changes the map
The Transformer made attention the core primitive, giving language models a cleaner way to learn context at scale.
- Status: Complete
Scale becomes the story
Large pretrained models showed that more data, compute, and parameters could unlock useful few-shot behavior.
- Status: Complete
Chat becomes the interface
Conversational assistants made LLMs feel less like research demos and more like everyday software.
- Status: Current
Models become teammates
LLMs now read, write, see, call tools, and coordinate multi-step work across product workflows.
Show sourceexamples/timeline/recipe-origin-story.tsx
"use client";
import { Timeline, type TimelineItemData } from "@dethink/components";
type OriginMilestone = {
year: string;
};
const originMilestones: TimelineItemData<OriginMilestone>[] = [
{
id: "transformer",
title: "Attention changes the map",
description:
"The Transformer made attention the core primitive, giving language models a cleaner way to learn context at scale.",
datetime: "2017-01-01T00:00:00Z",
dateLabel: "2017",
status: "complete",
marker: <span className="font-mono text-[10px] font-bold">T</span>,
data: { year: "2017" },
},
{
id: "scale",
title: "Scale becomes the story",
description:
"Large pretrained models showed that more data, compute, and parameters could unlock useful few-shot behavior.",
datetime: "2020-01-01T00:00:00Z",
dateLabel: "2020",
status: "complete",
marker: <span className="font-mono text-[10px] font-bold">S</span>,
data: { year: "2020" },
},
{
id: "chat",
title: "Chat becomes the interface",
description:
"Conversational assistants made LLMs feel less like research demos and more like everyday software.",
datetime: "2022-01-01T00:00:00Z",
dateLabel: "2022",
status: "complete",
marker: <span className="font-mono text-[10px] font-bold">C</span>,
data: { year: "2022" },
},
{
id: "agents",
title: "Models become teammates",
description:
"LLMs now read, write, see, call tools, and coordinate multi-step work across product workflows.",
datetime: "2026-01-01T00:00:00Z",
dateLabel: "2026",
status: "current",
marker: <span className="font-mono text-[10px] font-bold">A</span>,
data: { year: "2026" },
},
];
export function TimelineRecipeOriginStory() {
return (
<Timeline<OriginMilestone>
aria-label="Story of LLMs"
items={originMilestones}
mode="story"
/>
);
}Deployment history
Statuses carry rollout health — including a rollback marked error — and controlled selection drives a details panel showing each deployment's commit and author from its typed payload.
- Status: Complete
v1.4.0 → production
Date suite components released.
- Status: Warning
v1.4.1 → canary
Latency regression flagged on p99.
- Status: Error
v1.4.1 → production
Rolled back after checkout error spike.
- Status: Complete
v1.4.2 → production
Hotfix for date parsing in Safari.
v1.4.1 → production
Commit 38385e3 · deployed by Miguel
Show sourceexamples/timeline/recipe-deploy-history.tsx
"use client";
import { useState } from "react";
import { Timeline, type TimelineItemData } from "@dethink/components";
type DeployPayload = { sha: string; author: string };
const deploys: TimelineItemData<DeployPayload>[] = [
{
id: "d-2214",
title: "v1.4.2 → production",
description: "Hotfix for date parsing in Safari.",
datetime: "2026-07-04T08:41:00Z",
dateLabel: "Today 08:41",
status: "complete",
data: { sha: "f642257", author: "Dana" },
},
{
id: "d-2213",
title: "v1.4.1 → production",
description: "Rolled back after checkout error spike.",
datetime: "2026-07-03T16:20:00Z",
dateLabel: "Yesterday 16:20",
status: "error",
data: { sha: "38385e3", author: "Miguel" },
},
{
id: "d-2212",
title: "v1.4.1 → canary",
description: "Latency regression flagged on p99.",
datetime: "2026-07-03T14:05:00Z",
dateLabel: "Yesterday 14:05",
status: "warning",
data: { sha: "38385e3", author: "Miguel" },
},
{
id: "d-2211",
title: "v1.4.0 → production",
description: "Date suite components released.",
datetime: "2026-07-01T11:32:00Z",
dateLabel: "Jul 1 11:32",
status: "complete",
data: { sha: "17f7ab3", author: "Priya" },
},
];
/**
* An interactive deployment history: statuses carry the health of each
* rollout, and selecting an event surfaces its payload — commit and author —
* in a details panel driven by the controlled selection.
*/
export function TimelineRecipeDeployHistory() {
const [selectedId, setSelectedId] = useState<string | null>("d-2213");
const selected = deploys.find((deploy) => deploy.id === selectedId);
return (
<div className="space-y-4">
<Timeline
aria-label="Deployment history"
items={deploys}
selectedId={selectedId}
onSelectedIdChange={setSelectedId}
/>
<div
aria-live="polite"
className="border-border mx-auto max-w-md rounded-lg border px-4 py-3 text-sm"
>
{selected ? (
<>
<p className="font-medium">{selected.title}</p>
<p className="text-muted-foreground mt-0.5">
Commit <span className="font-mono">{selected.data?.sha}</span> ·
deployed by {selected.data?.author}
</p>
</>
) : (
<p className="text-muted-foreground">
Select a deployment for details.
</p>
)}
</div>
</div>
);
}Timeline now defaults to a vertical activity list. Set presentation="canvas" to retain pan and zoom, or variant="cards" for bordered flow cards. Reveal defaults are 220ms with a 60ms interval; pass revealOptions to keep your previous timing.
Grouping and expandable details are flow features. Custom renderers remain supported; selection and disclosure controls are separate from links and actions. TimelineFeed installs separately as timeline-feed and owns its scroll panel.
Timeline is data-driven: items carry the content, and the component handles layout, scale, and interaction.
| Prop | What it does | Default |
|---|---|---|
variant"activity" | "cards" | Compact activity rows or bordered cards in non-story flow layouts. | "activity" |
getGroup(item) => { id: string; label: ReactNode } | null | Labels contiguous groups after ordering, without reordering events. Flow only. | Not set |
details / renderDetailsReactNode / TimelineItemRenderer | Item details or a typed details renderer, exposed through a separate disclosure. Flow only. | Not set |
expandedIds / defaultExpandedIds / onExpandedIdsChangestring[] / string[] / (ids) => void | Controlled or uncontrolled expansion; multiple events may be open. | [] |
TimelineFeed: followLatest / viewportClassNameboolean / string | Contained live feed. Following pauses away from the end; the viewport class customizes its height. | true / h-[28rem] |
itemsTimelineItemData[] | Event data: id, title, description, datetime/dateLabel, status, optional marker and typed payload via data. | Not set |
mode"events" | "progress" | "story" | Dated event history, undated progress sequence, or static editorial story timeline. | "events" |
status (per item)"neutral" | "complete" | "current" | "upcoming" | "warning" | "error" | Health/progress tone of each item's marker and card. | "neutral" |
orientation / layout"horizontal" | "vertical" / layout variants | Axis of the track and how item cards stack around it. | vertical (flow), horizontal (canvas) |
scale / ordertime scale / chronological order | How datetimes map to track distance and which direction time flows. | auto |
selectedId / defaultSelectedId / onSelectedIdChangestring | null / string | null / (id) => void | Controlled or uncontrolled selection; arrow keys move between items. | Not set |
presentation"canvas" | "flow" | Pannable/zoomable plane, or a static document-flow list with markers, rail, and compact cards. | "flow" |
reveal / revealOptions"none" | "stagger" | "all" / trigger, interval, duration, initialDelay | Animated item entrance for the flow presentation — staggered or all at once, on mount, in view, or manually. | "none" |
revealCount / onItemReveal / onRevealCompletenumber / (id, index) => void / () => void | Controlled reveal progression for the manual trigger, plus per-item and completion callbacks. Appended items animate in as their own batch. | Not set |
interactiveboolean | Disables selection for purely presentational timelines. | true |
viewportTimelineViewportOptions | Zoom and pan options for long histories (defaultZoom, limits). | Not set |
renderItemTimelineItemRenderer | Custom item rendering with access to the typed payload. | built-in card |