Product Roadmap
See what we've built and what's coming next on our journey
Completed March 2024
Platform Launch
Initial release with core features and essential functionality for early adopters.
Completed June 2024
Team Collaboration
Enhanced collaboration features to help teams work together more effectively.
In Progress - Expected September 2024
Advanced Analytics
Powerful analytics and insights to help you make data-driven decisions.
Expected December 2024
Enterprise Features
Enterprise-grade security, compliance, and scalability features.
Planned for Q1 2025
AI-Powered Automation
Intelligent automation to streamline workflows and boost productivity.
Planned for Q2 2025
Mobile Applications
Native mobile apps for iOS and Android with offline capabilities.
Planned for Q3 2025
Global Expansion
Multi-language support and regional data centers for global reach.
Planned for Q4 2025
Performance & Scale
Infrastructure improvements for lightning-fast performance at any scale.
2
Shipped
2
In Progress
4
Planned
Installation
Install dependencies
npm install framer-motion lucide-reactCopy and paste the following code into your project.
"use client";
import React from "react";
import { motion } from "framer-motion";
import { Check, Clock, Sparkles, ArrowRight } from "lucide-react";
interface RoadmapItem {
id: string;
quarter: string;
title: string;
description: string;
status: "completed" | "in-progress" | "planned";
features: string[];
icon?: React.ElementType;
date?: string;
}
interface RoadmapTimelineProps {
title?: string;
description?: string;
items: RoadmapItem[];
className?: string;
}
export function RoadmapTimeline({
title = "Product Roadmap",
description = "See what we're building and what's coming next",
items,
className = "",
}: RoadmapTimelineProps) {
const getStatusConfig = (status: RoadmapItem["status"]) => {
switch (status) {
case "completed":
return {
label: "Shipped",
color: "text-emerald-600 dark:text-emerald-400",
bg: "bg-emerald-50 dark:bg-emerald-950/30",
border: "border-emerald-200 dark:border-emerald-800",
dotBg: "bg-emerald-500",
icon: Check,
};
case "in-progress":
return {
label: "In Progress",
color: "text-blue-600 dark:text-blue-400",
bg: "bg-blue-50 dark:bg-blue-950/30",
border: "border-blue-200 dark:border-blue-800",
dotBg: "bg-blue-500",
icon: Clock,
};
case "planned":
return {
label: "Planned",
color: "text-zinc-600 dark:text-zinc-400",
bg: "bg-zinc-50 dark:bg-zinc-900",
border: "border-zinc-200 dark:border-zinc-800",
dotBg: "bg-zinc-400",
icon: Sparkles,
};
}
};
return (
<section className={`w-full py-24 ${className}`}>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, ease: "easeOut" }}
className="mb-16 text-center"
>
<h2 className="mb-4 text-4xl font-bold tracking-tight text-zinc-900 dark:text-white sm:text-5xl">
{title}
</h2>
<p className="mx-auto max-w-2xl text-lg text-zinc-600 dark:text-zinc-400">
{description}
</p>
</motion.div>
<div className="relative space-y-8">
{items.map((item, index) => {
const config = getStatusConfig(item.status);
const StatusIcon = config.icon;
const ItemIcon = item.icon;
const isLast = index === items.length - 1;
return (
<motion.div
key={item.id}
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{
duration: 0.5,
delay: index * 0.1,
ease: "easeOut",
}}
className="relative flex gap-8"
>
<div className="relative flex flex-col items-center">
<div
className={`z-10 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full border-4 border-white shadow-lg dark:border-zinc-950 ${config.dotBg}`}
>
<StatusIcon className="h-5 w-5 text-white" />
</div>
{!isLast && (
<div className="absolute top-12 h-full w-0.5 bg-zinc-200 dark:bg-zinc-800" />
)}
</div>
<div className="flex-1 pb-8">
<div className="overflow-hidden rounded-2xl border border-zinc-200 bg-white p-6 shadow-sm transition-all duration-300 hover:shadow-md dark:border-zinc-800 dark:bg-zinc-900 sm:p-8">
<div className="mb-4 flex flex-wrap items-start justify-between gap-4">
<div>
<div className="mb-2 flex items-center gap-3">
<span className="text-sm font-semibold text-zinc-500 dark:text-zinc-400">
{item.quarter}
</span>
<span
className={`inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold ${config.bg} ${config.color}`}
>
<StatusIcon className="h-3 w-3" />
{config.label}
</span>
</div>
{item.date && (
<p className="text-xs text-zinc-500 dark:text-zinc-500">
{item.date}
</p>
)}
</div>
{ItemIcon && (
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-zinc-100 dark:bg-zinc-800">
{React.createElement(ItemIcon, {
className:
"h-6 w-6 text-zinc-700 dark:text-zinc-300",
})}
</div>
)}
</div>
<h3 className="mb-2 text-2xl font-bold text-zinc-900 dark:text-white">
{item.title}
</h3>
<p className="mb-6 text-zinc-600 dark:text-zinc-400">
{item.description}
</p>
<div className="grid gap-2 sm:grid-cols-2">
{item.features.map((feature, idx) => (
<div
key={idx}
className="flex items-start gap-2 text-sm text-zinc-600 dark:text-zinc-400"
>
<ArrowRight className="mt-0.5 h-4 w-4 flex-shrink-0 text-zinc-400" />
<span>{feature}</span>
</div>
))}
</div>
</div>
</div>
</motion.div>
);
})}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.4, ease: "easeOut" }}
className="mt-16 flex flex-wrap justify-center gap-8"
>
{[
{
status: "completed" as const,
count: items.filter((i) => i.status === "completed").length,
},
{
status: "in-progress" as const,
count: items.filter((i) => i.status === "in-progress").length,
},
{
status: "planned" as const,
count: items.filter((i) => i.status === "planned").length,
},
].map(({ status, count }) => {
const config = getStatusConfig(status);
const Icon = config.icon;
return (
<div
key={status}
className="flex items-center gap-3 rounded-xl border border-zinc-200 bg-white px-6 py-3 dark:border-zinc-800 dark:bg-zinc-900"
>
<div
className={`flex h-10 w-10 items-center justify-center rounded-lg ${config.bg}`}
>
<Icon className={`h-5 w-5 ${config.color}`} />
</div>
<div>
<p className="text-2xl font-bold text-zinc-900 dark:text-white">
{count}
</p>
<p className="text-sm text-zinc-600 dark:text-zinc-400">
{config.label}
</p>
</div>
</div>
);
})}
</motion.div>
</div>
</section>
);
}Features
- ✅ Vertical timeline - Chronological flow from top to bottom
- ✅ Color-coded status - Completed (Green), In Progress (Blue), Planned (Gray)
- ✅ Connected dots - Visual line connecting all milestones
- ✅ Smooth animations - Sequential fade-in transitions
- ✅ Status indicators - Circular dots with status icons
- ✅ Clean cards - Modern card design for each milestone
- ✅ Stats summary - Shows counts by status at bottom
- ✅ Responsive design - Works beautifully on all screen sizes
- ✅ Dark mode ready - Elegant in both themes
- ✅ TypeScript support - Full type safety
Usage
Basic Usage
import RoadmapTimeline from "@/components/ui/roadmap-timeline";
import {Rocket, Users, BarChart3} from "lucide-react";
const roadmapItems = [
{
id: "q1-2024",
quarter: "Q1 2024",
title: "Platform Launch",
description: "Initial release with core features.",
status: "completed",
date: "Completed March 2024",
icon: Rocket,
features: ["User authentication", "Dashboard analytics", "API integration"],
},
{
id: "q2-2024",
quarter: "Q2 2024",
title: "Team Collaboration",
description: "Enhanced collaboration features.",
status: "in-progress",
date: "Expected June 2024",
icon: Users,
features: ["Team workspaces", "Real-time collaboration", "Activity feed"],
},
{
id: "q3-2024",
quarter: "Q3 2024",
title: "Advanced Analytics",
description: "Powerful analytics and insights.",
status: "planned",
date: "Planned for Q3 2024",
icon: BarChart3,
features: [
"Custom dashboards",
"Predictive analytics",
"Automated reports",
],
},
];
export default function Page() {
return <RoadmapTimeline items={roadmapItems} />;
}Custom Title and Description
<RoadmapTimeline
title="Our Journey"
description="See what we've built and what's coming next"
items={roadmapItems}
/>Props
RoadmapTimelineProps
| Prop | Type | Default | Description |
|---|---|---|---|
items | RoadmapItem[] | Required | Array of roadmap items |
title | string | "Product Roadmap" | Section title |
description | string | "See what we're building..." | Section description |
className | string | "" | Additional CSS classes |
RoadmapItem
| Prop | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier |
quarter | string | Yes | Quarter label (e.g., "Q1 2024") |
title | string | Yes | Item title |
description | string | Yes | Item description |
status | "completed" | "in-progress" | "planned" | Yes | Current status |
features | string[] | Yes | List of features |
icon | React.ElementType | No | Lucide React icon component |
date | string | No | Date or timeline info |
TypeScript Interface
interface RoadmapItem {
id: string;
quarter: string;
title: string;
description: string;
status: "completed" | "in-progress" | "planned";
features: string[];
icon?: React.ElementType;
date?: string;
}
interface RoadmapTimelineProps {
title?: string;
description?: string;
items: RoadmapItem[];
className?: string;
}Use Cases
Perfect for:
- Product roadmap pages
- Feature announcement pages
- About pages (company timeline)
- Changelog pages
- Development progress tracking
- Investor presentations
- Customer transparency
- Team alignment
Customization
Change Colors
// In the component, modify getStatusConfig function
case "completed":
return {
label: "Shipped",
color: "text-purple-600 dark:text-purple-400", // Your brand color
bg: "bg-purple-50 dark:bg-purple-950/30",
border: "border-purple-200 dark:border-purple-800",
dotBg: "bg-purple-500",
icon: Check,
};Change Timeline Dot Size
// Modify the dot size
<div className="flex h-16 w-16 items-center justify-center rounded-full">
{/* Larger: h-16 w-16 */}
{/* Default: h-12 w-12 */}
{/* Smaller: h-10 w-10 */}
</div>Add More Status Types
// Add a new status type
interface RoadmapItem {
status: "completed" | "in-progress" | "planned" | "cancelled";
// ...
}
// Add to getStatusConfig
case "cancelled":
return {
label: "Cancelled",
color: "text-red-600 dark:text-red-400",
bg: "bg-red-50 dark:bg-red-950/30",
border: "border-red-200 dark:border-red-800",
dotBg: "bg-red-500",
icon: XCircle,
};Change Timeline Line Style
// Make the line thicker
<div className="absolute top-12 h-full w-1 bg-zinc-200" />
// Make it dashed
<div className="absolute top-12 h-full w-0.5 border-l-2 border-dashed border-zinc-300" />
// Add gradient
<div className="absolute top-12 h-full w-0.5 bg-gradient-to-b from-emerald-500 via-blue-500 to-zinc-300" />Change Feature Grid Layout
// Show features in single column
<div className="grid gap-2">
{/* Single column */}
</div>
// Show features in 3 columns
<div className="grid gap-2 sm:grid-cols-3">
{/* 3 columns on larger screens */}
</div>Remove Stats Summary
// If you don't want the stats at the bottom
// Simply remove or comment out the stats section:
{
/* Stats */
}
{
/* <motion.div className="mt-16...">
...
</motion.div> */
}Common Patterns
Yearly Roadmap
const roadmap2024 = [
{
id: "jan-2024",
quarter: "January",
title: "Q1 Features",
// ...
},
{
id: "apr-2024",
quarter: "April",
title: "Q2 Features",
// ...
},
];Company Timeline
const companyTimeline = [
{
id: "founded",
quarter: "2020",
title: "Company Founded",
description: "Started with a vision to change the industry.",
status: "completed",
icon: Rocket,
features: [
"Incorporated in Delaware",
"Raised seed funding",
"Hired first employees",
],
},
{
id: "series-a",
quarter: "2022",
title: "Series A Funding",
description: "Raised $10M to scale the team.",
status: "completed",
icon: TrendingUp,
features: [
"Led by Sequoia Capital",
"Grew team to 50 people",
"Expanded to 3 countries",
],
},
];Feature Releases
const featureReleases = [
{
id: "v1",
quarter: "v1.0",
title: "Initial Release",
description: "First public version.",
status: "completed",
date: "Released Jan 2024",
icon: Package,
features: ["Core functionality", "Basic UI", "API v1"],
},
{
id: "v2",
quarter: "v2.0",
title: "Major Update",
description: "Complete redesign and new features.",
status: "in-progress",
date: "Expected Mar 2024",
icon: Sparkles,
features: ["New UI", "Advanced features", "API v2"],
},
];Examples
SaaS Product Roadmap
const saasRoadmap = [
{
id: "q1",
quarter: "Q1 2024",
title: "Core Platform",
description: "Essential features for launch.",
status: "completed",
icon: Rocket,
features: [
"User authentication",
"Dashboard",
"API integration",
"Email notifications",
],
},
{
id: "q2",
quarter: "Q2 2024",
title: "Team Features",
description: "Collaboration and team management.",
status: "in-progress",
icon: Users,
features: [
"Team workspaces",
"Role-based permissions",
"Activity feed",
"Shared templates",
],
},
{
id: "q3",
quarter: "Q3 2024",
title: "Enterprise",
description: "Enterprise-grade features.",
status: "planned",
icon: Shield,
features: [
"SSO integration",
"Advanced security",
"Audit logs",
"Custom SLA",
],
},
];Mobile App Roadmap
const mobileRoadmap = [
{
id: "beta",
quarter: "Beta",
title: "Beta Release",
description: "Limited beta for testing.",
status: "completed",
date: "Released Feb 2024",
icon: TestTube,
features: ["iOS app", "Core features", "Push notifications"],
},
{
id: "v1",
quarter: "v1.0",
title: "Public Launch",
description: "Full public release.",
status: "in-progress",
date: "Expected Apr 2024",
icon: Smartphone,
features: ["Android app", "Offline mode", "Widget support"],
},
];Analytics Integration
// Track which roadmap items users view
const trackRoadmapView = (itemId: string) => {
analytics.track("roadmap_item_viewed", {
item_id: itemId,
timestamp: Date.now(),
});
};
// Track when users reach the roadmap section
useEffect(() => {
analytics.track("roadmap_section_viewed");
}, []);Tips
- Update quarterly - Keep your roadmap fresh
- Be honest - Transparency builds trust
- Show progress - Move items through statuses
- Limit scope - Don't show too far ahead
- Link to changelog - Connect roadmap to releases
- Gather input - Ask users what they want
- Celebrate milestones - Highlight achievements