Timeline visualizes a series of chained events.
import { Timeline } from 'primereact/timeline';
<Timeline>
<Timeline.Event>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
<Timeline.Connector />
</Timeline.Separator>
<Timeline.Content />
</Timeline.Event>
</Timeline>
import { Timeline } from 'primereact/timeline';
export default function BasicDemo() {
const events = [{ status: 'Ordered' }, { status: 'Processing' }, { status: 'Shipped' }, { status: 'Delivered' }];
return (
<div className="card">
<Timeline>
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event.status}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
</div>
);
}
Content location relative the line is defined with the align property.
import { Timeline } from 'primereact/timeline';
export default function AlignmentDemo() {
const events = [{ status: 'Ordered' }, { status: 'Processing' }, { status: 'Shipped' }, { status: 'Delivered' }];
return (
<div className="card flex flex-wrap gap-12">
<Timeline className="w-full md:w-80">
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event.status}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
<Timeline align="right" className="w-full md:w-80">
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event.status}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
<Timeline align="alternate" className="w-full md:w-80">
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event.status}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
</div>
);
}
Additional content at the other side of the line can be provided with the opposite property.
import { Timeline } from 'primereact/timeline';
export default function OppositeDemo() {
const events = [
{ status: 'Ordered', date: '15/10/2020 10:30' },
{ status: 'Processing', date: '15/10/2020 14:00' },
{ status: 'Shipped', date: '15/10/2020 16:15' },
{ status: 'Delivered', date: '16/10/2020 10:00' }
];
return (
<div className="card">
<Timeline>
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite>
<small className="text-surface-500 dark:text-surface-400">{event.date}</small>
</Timeline.Opposite>
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event.status}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
</div>
);
}
TimeLine orientation is controlled with the orientation property, default is vertical having horizontal as the alternative.
import { Timeline } from 'primereact/timeline';
export default function HorizontalDemo() {
const events = ['2020', '2021', '2022', '2023'];
return (
<div className="card flex flex-col gap-4">
<Timeline orientation="horizontal" align="top">
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
<Timeline orientation="horizontal" align="bottom">
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite />
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
<Timeline orientation="horizontal" align="alternate">
{events.map((event, index) => (
<Timeline.Event key={index}>
<Timeline.Opposite> </Timeline.Opposite>
<Timeline.Separator>
<Timeline.Marker />
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content>{event}</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
</div>
);
}
Sample implementation with custom content and styled markers.
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque quas!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque quas!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque quas!
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis esse, cupiditate neque quas!
import { Button } from 'primereact/button';
import { Card } from 'primereact/card';
import { Timeline } from 'primereact/timeline';
export default function CustomDemo() {
const events = [
{ status: 'Ordered', date: '15/10/2020 10:30', icon: 'pi pi-shopping-cart', color: '#9C27B0', image: 'game-controller.jpg' },
{ status: 'Processing', date: '15/10/2020 14:00', icon: 'pi pi-cog', color: '#673AB7' },
{ status: 'Shipped', date: '15/10/2020 16:15', icon: 'pi pi-shopping-cart', color: '#FF9800' },
{ status: 'Delivered', date: '16/10/2020 10:00', icon: 'pi pi-check', color: '#607D8B' }
];
return (
<div className="card">
<Timeline align="alternate">
{events.map((event, index) => (
<Timeline.Event key={index} className={index % 2 === 1 ? 'max-[960px]:flex-row' : undefined}>
<Timeline.Opposite />
<Timeline.Separator>
<span className={['flex w-8 h-8 items-center justify-center rounded-full z-10 shadow-sm', index !== events.length - 1 ? 'text-white bg-primary' : ''].join(' ')}>
<i className={event.icon}></i>
</span>
{index !== events.length - 1 && <Timeline.Connector />}
</Timeline.Separator>
<Timeline.Content className={index % 2 === 1 ? 'max-[960px]:!text-left' : undefined}>
<Card className="mt-4">
<Card.Body>
<Card.Caption>
<Card.Title>{event.status}</Card.Title>
<Card.Subtitle>{event.date}</Card.Subtitle>
</Card.Caption>
<Card.Content>
{event.image && <img src={`https://primefaces.org/cdn/primevue/images/product/${event.image}`} alt={event.status} width="200" className="shadow-sm" />}
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore sed consequuntur error repudiandae numquam deserunt quisquam repellat libero asperiores earum nam nobis, culpa ratione quam perferendis
esse, cupiditate neque quas!
</p>
<Button variant="text">Read more</Button>
</Card.Content>
</Card.Body>
</Card>
</Timeline.Content>
</Timeline.Event>
))}
</Timeline>
</div>
);
}