All components
Timeline
data-displayAceternity UI component.
responsive · 620px
Install
Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:
$
npx shadcn@latest add https://your-domain/r/timeline.jsonUsage
"use client";
import { Timeline } from "@/registry/aceternity-ui/timeline";
export default function Demo() {
const data = [
{
title: "2024",
content: (
<div>
<p className="text-neutral-800 dark:text-neutral-200 text-xs md:text-sm font-normal mb-4">
Launched the new design system and component library with 50+ components.
</p>
<div className="grid grid-cols-2 gap-4">
<img
src="https://picsum.photos/seed/timeline1/500/300"
alt="Design system"
className="rounded-lg object-cover w-full h-32"
/>
<img
src="https://picsum.photos/seed/timeline2/500/300"
alt="Components"
className="rounded-lg object-cover w-full h-32"
/>
</div>
</div>
),
},
{
title: "Early 2023",
content: (
<div>
<p className="text-neutral-800 dark:text-neutral-200 text-xs md:text-sm font-normal mb-4">
Joined a fast-growing startup as a senior frontend engineer.
</p>
<p className="text-neutral-800 dark:text-neutral-200 text-xs md:text-sm font-normal mb-4">
Built and shipped the core product features that drove 3x user growth.
</p>
<div className="grid grid-cols-2 gap-4">
<img
src="https://picsum.photos/seed/timeline3/500/300"
alt="Startup"
className="rounded-lg object-cover w-full h-32"
/>
<img
src="https://picsum.photos/seed/timeline4/500/300"
alt="Team"
className="rounded-lg object-cover w-full h-32"
/>
</div>
</div>
),
},
{
title: "2022",
content: (
<div>
<p className="text-neutral-800 dark:text-neutral-200 text-xs md:text-sm font-normal mb-4">
Graduated with a degree in Computer Science and started freelancing.
</p>
<div className="grid grid-cols-2 gap-4">
<img
src="https://picsum.photos/seed/timeline5/500/300"
alt="Graduation"
className="rounded-lg object-cover w-full h-32"
/>
<img
src="https://picsum.photos/seed/timeline6/500/300"
alt="Work"
className="rounded-lg object-cover w-full h-32"
/>
</div>
</div>
),
},
{
title: "2020",
content: (
<div>
<p className="text-neutral-800 dark:text-neutral-200 text-xs md:text-sm font-normal mb-4">
Started learning web development through online courses and personal projects.
</p>
<img
src="https://picsum.photos/seed/timeline7/500/300"
alt="Learning"
className="rounded-lg object-cover w-full h-32 mb-4"
/>
</div>
),
},
];
return (
<div className="w-full">
<Timeline data={data} />
</div>
);
}Component source
"use client";
import {
useMotionValueEvent,
useScroll,
useTransform,
motion,
} from "motion/react";
import React, { useEffect, useRef, useState } from "react";
interface TimelineEntry {
title: string;
content: React.ReactNode;
}
export const Timeline = ({ data }: { data: TimelineEntry[] }) => {
const ref = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState(0);
useEffect(() => {
if (ref.current) {
const rect = ref.current.getBoundingClientRect();
setHeight(rect.height);
}
}, [ref]);
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ["start 10%", "end 50%"],
});
const heightTransform = useTransform(scrollYProgress, [0, 1], [0, height]);
const opacityTransform = useTransform(scrollYProgress, [0, 0.1], [0, 1]);
return (
<div
className="w-full bg-white dark:bg-neutral-950 font-sans md:px-10"
ref={containerRef}
>
<div className="max-w-7xl mx-auto py-20 px-4 md:px-8 lg:px-10">
<h2 className="text-lg md:text-4xl mb-4 text-black dark:text-white max-w-4xl">
Changelog from my journey
</h2>
<p className="text-neutral-700 dark:text-neutral-300 text-sm md:text-base max-w-sm">
I've been working on Aceternity for the past 2 years. Here's
a timeline of my journey.
</p>
</div>
<div ref={ref} className="relative max-w-7xl mx-auto pb-20">
{data.map((item, index) => (
<div
key={index}
className="flex justify-start pt-10 md:pt-40 md:gap-10"
>
<div className="sticky flex flex-col md:flex-row z-40 items-center top-40 self-start max-w-xs lg:max-w-sm md:w-full">
<div className="h-10 absolute left-3 md:left-3 w-10 rounded-full bg-white dark:bg-black flex items-center justify-center">
<div className="h-4 w-4 rounded-full bg-neutral-200 dark:bg-neutral-800 border border-neutral-300 dark:border-neutral-700 p-2" />
</div>
<h3 className="hidden md:block text-xl md:pl-20 md:text-5xl font-bold text-neutral-500 dark:text-neutral-500 ">
{item.title}
</h3>
</div>
<div className="relative pl-20 pr-4 md:pl-4 w-full">
<h3 className="md:hidden block text-2xl mb-4 text-left font-bold text-neutral-500 dark:text-neutral-500">
{item.title}
</h3>
{item.content}{" "}
</div>
</div>
))}
<div
style={{
height: height + "px",
}}
className="absolute md:left-8 left-8 top-0 overflow-hidden w-[2px] bg-[linear-gradient(to_bottom,var(--tw-gradient-stops))] from-transparent from-[0%] via-neutral-200 dark:via-neutral-700 to-transparent to-[99%] [mask-image:linear-gradient(to_bottom,transparent_0%,black_10%,black_90%,transparent_100%)] "
>
<motion.div
style={{
height: heightTransform,
opacity: opacityTransform,
}}
className="absolute inset-x-0 top-0 w-[2px] bg-gradient-to-t from-purple-500 via-blue-500 to-transparent from-[0%] via-[10%] rounded-full"
/>
</div>
</div>
</div>
);
};Dependencies
motion
Source: Aceternity UI