Timeline
Một danh sách sự kiện có thứ tự, như các bước của một lượt chạy agent hay lịch sử một đơn hàng: mỗi sự kiện có điểm đánh dấu (chấm, icon hoặc avatar) tô theo trạng thái, đường nối tới sự kiện sau, tiêu đề, thời gian, mô tả và nội dung tùy ý. Mặc định dọc, hoặc ngang thành một hàng.
Xem trước
import { IconFileText, IconLoader2, IconSearch } from "@tabler/icons-react"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>Cách dùng
Khi nào dùng
- Hiện các sự kiện theo thứ tự đã xảy ra, như các bước của một lượt chạy agent, một lần deploy hay lịch sử đơn hàng.
- Một luồng hoạt động, mỗi mục nói ai đã làm gì và khi nào.
Khi nào không dùng
- Các bước của một luồng người dùng đi qua. Dùng Stepper.
- Danh sách không có thứ tự thời gian. Dùng danh sách hoặc Table.
- Một trạng thái đơn lẻ. Dùng Badge hoặc Alert.
Biến thể
orientation
vertical xếp sự kiện từ trên xuống; horizontal đặt chúng thành các cột đều nhau, cuộn ngang khi hẹp.
import { IconFileText, IconLoader2, IconSearch } from "@tabler/icons-react"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline orientation="horizontal" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>size
Cỡ ô đánh dấu, chữ và khoảng cách: điểm đánh dấu 24, 32 hoặc 40px.
import { IconFileText, IconLoader2, IconSearch } from "@tabler/icons-react"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline size="sm" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline size="lg" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>status
Trên TimelineItem. Tô điểm đánh dấu bằng token trạng thái.
import { IconFileText, IconLoader2, IconSearch } from "@tabler/icons-react"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline status="success" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline status="warning" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline status="danger" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>
<Timeline status="info" statusLabels={{ success: "xong", warning: "cần chú ý", danger: "thất bại", info: "đang chạy" }}>
<TimelineItem status="success" icon={<IconSearch />} title="Đã tìm trong tài liệu" time="10:42" dateTime="2026-10-24T10:42" description="Tìm thấy 4 trang về token." />
<TimelineItem status="success" icon={<IconFileText />} title="Đã đọc tokens.css" time="10:43" dateTime="2026-10-24T10:43" description="312 dòng, 48 token màu." />
<TimelineItem status="info" icon={<IconLoader2 />} title="Đang viết câu trả lời" time="10:43" dateTime="2026-10-24T10:43" description="Bước hiện tại." current />
</Timeline>Ví dụ
Lượt chạy agent
Icon trong vòng tròn nhạt theo trạng thái cho các bước, bước hiện tại được đánh dấu.
import { IconFileText, IconLoader2, IconSearch } from "@tabler/icons-react"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline>
<TimelineItem status="success" icon={<IconSearch />} title="Searched the docs" time="10:42" dateTime="2026-10-24T10:42" description="Found 4 pages about tokens." />
<TimelineItem status="success" icon={<IconFileText />} title="Read tokens.css" time="10:43" />
<TimelineItem status="info" icon={<IconLoader2 />} title="Writing the answer" time="10:43" current />
</Timeline>Lịch sử đơn hàng
Chấm tô theo trạng thái, có mô tả dưới mỗi sự kiện.
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline>
<TimelineItem status="success" title="Delivered" time="Oct 24" description="Left at the front desk." />
<TimelineItem status="warning" title="Delivery delayed" time="Oct 23" description="Weather on the route." />
<TimelineItem title="Shipped" time="Oct 21" />
<TimelineItem title="Order placed" time="20 thg 10" />
</Timeline>Trạng thái
Mỗi trạng thái tô chấm (đậm) hoặc vòng icon (nhạt) bằng token trạng thái của nó.
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline>
<TimelineItem title="Default" time="09:00" />
<TimelineItem status="success" title="Success" time="09:05" />
<TimelineItem status="warning" title="Warning" time="09:10" />
<TimelineItem status="danger" title="Danger" time="09:15" />
<TimelineItem status="info" title="Info" time="09:20" />
</Timeline>Hoạt động với avatar
Avatar cho biết ai đã làm; nội dung chứa mọi thứ dưới mô tả.
import { Avatar } from "@/components/ui/avatar"
import { Card } from "@/components/ui/card"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline>
<TimelineItem avatar={<Avatar alt="" color="blue">LT</Avatar>} title="Linh commented" time="2 hours ago">
<Card className="p-md text-sm">Can we keep the old name for one release?</Card>
</TimelineItem>
<TimelineItem avatar={<Avatar alt="" color="green">AP</Avatar>} title="An opened the pull request" time="Hôm qua" />
</Timeline>Bước thất bại
danger đánh dấu sự kiện thất bại; đặt nút thử lại trong nội dung.
import { IconCheck, IconX } from "@tabler/icons-react"
import { Button } from "@/components/ui/button"
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline>
<TimelineItem status="success" icon={<IconCheck />} title="Xây dựng" time="1m 12s" />
<TimelineItem status="danger" icon={<IconX />} title="Deploy" time="8s" description="The health check timed out.">
<Button size="sm" variant="outline">Thử lại</Button>
</TimelineItem>
</Timeline>Ngang
orientation="horizontal" đặt sự kiện thành các cột đều, cuộn ngang khi hẹp.
import { Timeline, TimelineItem } from "@/components/ui/timeline"
<Timeline orientation="horizontal">
<TimelineItem status="success" title="Placed" time="20 thg 10" />
<TimelineItem status="success" title="Shipped" time="Oct 21" />
<TimelineItem status="info" title="Out for delivery" time="Oct 24" current />
<TimelineItem title="Delivered" />
</Timeline>Nên và không nên
Nên
- Xếp sự kiện theo thứ tự đã xảy ra, cũ trước, trừ khi mới nhất quan trọng hơn, như một luồng hoạt động.
- Dùng status cho kết quả của sự kiện, và nói điều đó trong tiêu đề.
- Mỗi Timeline nên dùng một loại điểm đánh dấu; chỉ trộn icon và avatar khi ai làm là quan trọng.
Không nên
- Đừng dùng Timeline cho các bước của một luồng người dùng đi qua; dùng Stepper.
- Đừng chỉ dựa vào màu điểm đánh dấu; statusLabels và tiêu đề mang ý nghĩa.
- Đừng đặt form dài trong một sự kiện; dẫn tới một trang khác.
Props
| Tên | Kiểu | Giá trị | Mặc định | Mô tả |
|---|---|---|---|---|
| orientation | enum | vertical | horizontal | vertical | vertical xếp sự kiện từ trên xuống; horizontal đặt chúng thành các cột đều nhau, cuộn ngang khi hẹp. |
| size | enum | sm | md | lg | md | Cỡ ô đánh dấu, chữ và khoảng cách: điểm đánh dấu 24, 32 hoặc 40px. |
| statusLabels | { success?: string; warning?: string; danger?: string; info?: string } | - | - | Chữ được đọc sau tiêu đề và thời gian cho mỗi trạng thái. Mặc định: done, needs attention, failed, in progress. Tiếng Việt: xong, cần chú ý, thất bại, đang chạy. |
| status | enum | default | success | warning | danger | info | default | Trên TimelineItem. Tô điểm đánh dấu bằng token trạng thái. |
| icon | node | - | - | Trên TimelineItem. Một icon trong vòng tròn nhạt theo trạng thái, thay cho chấm. |
| avatar | node | - | - | Trên TimelineItem. Một Avatar thay cho chấm, như người đã thực hiện. Dùng cùng size với Timeline. |
| title | node | - | - | Trên TimelineItem. Việc đã xảy ra. |
| time | node | - | - | Trên TimelineItem. Thời điểm, như 10:42 hoặc 2 giờ trước. |
| dateTime | string | - | - | Trên TimelineItem. Ngày giờ máy đọc được cho time, như 2026-10-24T10:42. |
| description | node | - | - | Trên TimelineItem. Một hai dòng chi tiết dưới tiêu đề. |
| current | boolean | - | false | Trên TimelineItem. Đánh dấu sự kiện hiện tại bằng aria-current="step". |
| scrollLabel | string | - | Timeline | Tên vùng cuộn của timeline ngang, vùng này nhận focus bàn phím để cuộn. Mặc định "Timeline". |
Cấu tạo
| Phần | Mô tả |
|---|---|
| root | ol: các sự kiện xếp dọc, hoặc thành hàng cuộn ngang khi hẹp. |
| item | Một sự kiện, thẻ li: rail rồi phần thân. |
| rail | Điểm đánh dấu và đường nối tới điểm sau. Ẩn với trình đọc màn hình. |
| marker | Ô cỡ điểm đánh dấu (24, 32 hoặc 40px) chứa chấm, icon trong vòng tròn nhạt hoặc avatar, để tiêu đề thẳng hàng dù là loại nào. |
| connector | Đường 1px (border-strong) từ điểm đánh dấu này tới điểm sau, có khoảng hở ở hai đầu. Sự kiện cuối không có. |
| body | Hàng tiêu đề, mô tả và nội dung. |
| title | Việc đã xảy ra, chữ primary đậm vừa. |
| time | Thời điểm, chữ secondary với số tabular. Dọc: cùng hàng với tiêu đề, không rớt dòng. Ngang: luôn nằm dưới tiêu đề. |
| description | Một hai dòng chi tiết bằng chữ secondary. |
| content | Mọi thứ dưới mô tả, như Card, CodeBlock hoặc Button. |
Khả năng truy cập
Role: list
| Phím | Hành động |
|---|---|
| Tab | Đi qua các liên kết hoặc nút bên trong sự kiện. Bản thân danh sách không nhận focus. |
- Timeline là một danh sách có thứ tự, nên trình đọc màn hình đọc số sự kiện và vị trí của từng cái.
- Điểm đánh dấu và đường nối ẩn với trình đọc màn hình. Trạng thái khác default thêm chữ sau tiêu đề, EN `done`, `needs attention`, `failed`, `in progress`; đổi bằng statusLabels.
- current đánh dấu một sự kiện bằng aria-current="step".
- Truyền dateTime cho time để nó thành thẻ time với ngày giờ máy đọc được.
Cài đặt
npx werewolf-ui add timelineChạy npx werewolf-ui init một lần trong app trước khi thêm component. Init cài token, thêm CSS import và luật cho agent. Add cài component này cùng các dependency và giữ nguyên theme của bạn. Xem Cài đặt để biết yêu cầu và cách cài qua registry.