Saltar al contenido principal

Timeline

Eventos en orden, vertical u horizontal, con indicadores, fechas y estado completado.

  1. Project created

    Repository initialised from the starter template.
  2. First deploy

    Preview environment went live on the staging domain.
  3. Design review

    Tokens and spacing approved by the design team.
  4. Public launch

    Production release scheduled after QA sign-off.
1'use client';
2
3import { Timeline } from '@/components/ui/timeline';
4
5const EVENTS = [
6 {
7 step: 1,
8 date: 'Mar 12',
9 title: 'Project created',
10 body: 'Repository initialised from the starter template.',
11 },
12 {
13 step: 2,
14 date: 'Mar 14',
15 title: 'First deploy',
16 body: 'Preview environment went live on the staging domain.',
17 },
18 {
19 step: 3,
20 date: 'Mar 20',
21 title: 'Design review',
22 body: 'Tokens and spacing approved by the design team.',
23 },
24 {
25 step: 4,
26 date: 'Mar 28',
27 title: 'Public launch',
28 body: 'Production release scheduled after QA sign-off.',
29 },
30];
31
32export function Default() {
33 return (
34 <Timeline value={2} className="max-w-md">
35 {EVENTS.map((event) => (
36 <Timeline.Item key={event.step} step={event.step}>
37 <Timeline.Header>
38 <Timeline.Date>{event.date}</Timeline.Date>
39 <Timeline.Title>{event.title}</Timeline.Title>
40 </Timeline.Header>
41 <Timeline.Indicator />
42 <Timeline.Separator />
43 <Timeline.Content>{event.body}</Timeline.Content>
44 </Timeline.Item>
45 ))}
46 </Timeline>
47 );
48}

Instalación

pnpm dlx nachui add timeline

Anatomía

1import { Timeline } from '@/components/ui/timeline';
1<Timeline value={2}>
2 <Timeline.Item step={1}>
3 <Timeline.Header>
4 <Timeline.Date>12 mar</Timeline.Date>
5 <Timeline.Title>Proyecto creado</Timeline.Title>
6 </Timeline.Header>
7 <Timeline.Indicator />
8 <Timeline.Separator />
9 <Timeline.Content>Repositorio inicializado desde la plantilla base.</Timeline.Content>
10 </Timeline.Item>
11</Timeline>
Cada item declara su step. Los items cuyo step es menor o igual al value del timeline están completados: su indicador se rellena con el color primario y el separador que sale de ellos lo acompaña. El item en value además recibe data-active, así puedes darle estilo propio al paso actual. La raíz renderiza una lista ordenada y cada item un elemento de lista.

Composición

Usa la siguiente composición para construir un Timeline:
Timeline
└── Timeline.Item
├── Timeline.Header
│ ├── Timeline.Date
│ └── Timeline.Title
├── Timeline.Indicator
├── Timeline.Separator
└── Timeline.Content

Variantes

Horizontal

orientation="horizontal" distribuye los pasos de izquierda a derecha con la línea sobre el contenido. Los items reparten el ancho en partes iguales.
  1. Ordered

  2. Packed

  3. Shipped

  4. Delivered

1'use client';
2
3import { Timeline } from '@/components/ui/timeline';
4
5const STAGES = [
6 { step: 1, date: 'Mon', title: 'Ordered' },
7 { step: 2, date: 'Tue', title: 'Packed' },
8 { step: 3, date: 'Wed', title: 'Shipped' },
9 { step: 4, date: 'Fri', title: 'Delivered' },
10];
11
12export function Horizontal() {
13 return (
14 <Timeline orientation="horizontal" value={3} className="w-full max-w-lg">
15 {STAGES.map((stage) => (
16 <Timeline.Item key={stage.step} step={stage.step}>
17 <Timeline.Header>
18 <Timeline.Date>{stage.date}</Timeline.Date>
19 <Timeline.Title>{stage.title}</Timeline.Title>
20 </Timeline.Header>
21 <Timeline.Indicator />
22 <Timeline.Separator />
23 </Timeline.Item>
24 ))}
25 </Timeline>
26 );
27}

Íconos

Pasa un ícono como children del indicador y ajusta su tamaño con className. Agrega padding al item para que el contenido no choque con el marcador más grande.
  1. Commit pushed

    feat(ui): add a side-aware caret to the tooltip
  2. Checks passed

    Lint, type-check and 312 tests in 48s.
  3. Merged into main

    Squashed by lucia after one approval.
  4. Deployed

    Waiting for the production rollout window.
1'use client';
2
3import { GitCommitIcon, GitMergeIcon, Rocket01Icon, Tick02Icon } from '@hugeicons/core-free-icons';
4import { HugeiconsIcon } from '@hugeicons/react';
5import { Timeline } from '@/components/ui/timeline';
6
7const STEPS = [
8 {
9 step: 1,
10 icon: GitCommitIcon,
11 title: 'Commit pushed',
12 body: 'feat(ui): add a side-aware caret to the tooltip',
13 },
14 {
15 step: 2,
16 icon: Tick02Icon,
17 title: 'Checks passed',
18 body: 'Lint, type-check and 312 tests in 48s.',
19 },
20 {
21 step: 3,
22 icon: GitMergeIcon,
23 title: 'Merged into main',
24 body: 'Squashed by lucia after one approval.',
25 },
26 {
27 step: 4,
28 icon: Rocket01Icon,
29 title: 'Deployed',
30 body: 'Waiting for the production rollout window.',
31 },
32];
33
34export function Icons() {
35 return (
36 <Timeline value={3} className="max-w-md">
37 {STEPS.map((item) => (
38 <Timeline.Item key={item.step} step={item.step} className="ps-10">
39 <Timeline.Header>
40 <Timeline.Title>{item.title}</Timeline.Title>
41 </Timeline.Header>
42 <Timeline.Indicator className="bg-muted text-muted-foreground size-7 border-0 [&_svg]:size-4">
43 <HugeiconsIcon icon={item.icon} />
44 </Timeline.Indicator>
45 <Timeline.Separator />
46 <Timeline.Content>{item.body}</Timeline.Content>
47 </Timeline.Item>
48 ))}
49 </Timeline>
50 );
51}

Alternado

alternate refleja un item sí y otro no a cada lado de la línea, para roadmaps y páginas de historial.
  1. Discovery

    Interviews with twelve teams and a first prototype.
  2. Private beta

    Fifty workspaces onboarded behind a feature flag.
  3. General availability

    Self-serve signup, billing and the public docs.
  4. Integrations

    Slack, Linear and GitHub apps in the marketplace.
1'use client';
2
3import { Timeline } from '@/components/ui/timeline';
4
5const MILESTONES = [
6 {
7 step: 1,
8 date: 'Q1',
9 title: 'Discovery',
10 body: 'Interviews with twelve teams and a first prototype.',
11 },
12 {
13 step: 2,
14 date: 'Q2',
15 title: 'Private beta',
16 body: 'Fifty workspaces onboarded behind a feature flag.',
17 },
18 {
19 step: 3,
20 date: 'Q3',
21 title: 'General availability',
22 body: 'Self-serve signup, billing and the public docs.',
23 },
24 {
25 step: 4,
26 date: 'Q4',
27 title: 'Integrations',
28 body: 'Slack, Linear and GitHub apps in the marketplace.',
29 },
30];
31
32export function Alternate() {
33 return (
34 <Timeline alternate value={2} className="w-full max-w-xl">
35 {MILESTONES.map((milestone) => (
36 <Timeline.Item key={milestone.step} step={milestone.step}>
37 <Timeline.Header>
38 <Timeline.Date>{milestone.date}</Timeline.Date>
39 <Timeline.Title>{milestone.title}</Timeline.Title>
40 </Timeline.Header>
41 <Timeline.Indicator />
42 <Timeline.Separator />
43 <Timeline.Content>{milestone.body}</Timeline.Content>
44 </Timeline.Item>
45 ))}
46 </Timeline>
47 );
48}

Fechas a la izquierda

Desplaza el item con un margen y posiciona la fecha en el canal libre. Las piezas son elementos simples, así que con un par de clases alcanza.
  1. Ticket opened

    Customer reported a broken checkout on Safari.
  2. Assigned to Nico

    Escalated to the payments squad.
  3. Fix deployed

    Patched the Apple Pay session handshake.
  4. Resolved

    Customer confirmed the order went through.
1'use client';
2
3import { Timeline } from '@/components/ui/timeline';
4
5const ACTIVITY = [
6 {
7 step: 1,
8 date: '09:12',
9 title: 'Ticket opened',
10 body: 'Customer reported a broken checkout on Safari.',
11 },
12 { step: 2, date: '09:40', title: 'Assigned to Nico', body: 'Escalated to the payments squad.' },
13 {
14 step: 3,
15 date: '11:05',
16 title: 'Fix deployed',
17 body: 'Patched the Apple Pay session handshake.',
18 },
19 { step: 4, date: '11:30', title: 'Resolved', body: 'Customer confirmed the order went through.' },
20];
21
22export function LeftDates() {
23 return (
24 <Timeline value={4} className="max-w-lg">
25 {ACTIVITY.map((entry) => (
26 <Timeline.Item key={entry.step} step={entry.step} className="ms-16">
27 <Timeline.Header>
28 <Timeline.Date className="absolute -start-16 top-0.5 w-14 text-end font-mono">
29 {entry.date}
30 </Timeline.Date>
31 <Timeline.Title>{entry.title}</Timeline.Title>
32 </Timeline.Header>
33 <Timeline.Indicator />
34 <Timeline.Separator />
35 <Timeline.Content>{entry.body}</Timeline.Content>
36 </Timeline.Item>
37 ))}
38 </Timeline>
39 );
40}

Referencia de API

Timeline

PropTypeDefaultDescription
valuenumber1Los pasos menores o iguales a este valor están completados
orientation'vertical' | 'horizontal''vertical'Dirección del layout
alternatebooleanfalseRefleja un item de cada dos a través de la línea (vertical)
classNamestring-Clases CSS adicionales

Timeline.Item

PropTypeDefaultDescription
stepnumber-Posición del item, se compara contra value
classNamestring-Clases CSS adicionales
Expone data-completed y data-active para estilos.

Timeline.Title

PropTypeDefaultDescription
as'h2' | 'h3' | 'h4' | 'h5' | 'h6''h3'Nivel de encabezado
classNamestring-Clases CSS adicionales

Timeline.Date

Renderiza un elemento time y acepta dateTime además de className.

Timeline.Header, Timeline.Indicator, Timeline.Separator, Timeline.Content

Partes de layout de un item. Cada una acepta className y las props estándar de div. El indicador acepta children para un marcador personalizado.
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue
Ctrl+I