Everything you need in one platform
Features
- ✅ Color-coded tabs - Each tab has unique gradient colors (orange, blue, purple, green)
- ✅ Gradient icons - Beautiful gradient backgrounds for all icons
- ✅ Smooth animations - Zoom-in and fade transitions between tabs
- ✅ Gradient CTA buttons - Buttons match each tab's color scheme
- ✅ Active indicators - Animated underline and glow effects
- ✅ Feature cards - Detailed cards with gradient icons and checkmarks
- ✅ Hover effects - Scale, glow, and transform animations
- ✅ Image support - Optional images with hover zoom effect
- ✅ Responsive design - 4 columns on desktop, 2 on tablet, 1 on mobile
- ✅ Shadcn components - Built with Tabs, Card, Badge, Button
- ✅ TypeScript - Full type safety
- ✅ Production-ready - Premium polish, copy and paste to use
Installation
Copy and paste the following code into your project.
"use client";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ArrowRight, CheckCircle2, LucideIcon } from "lucide-react";
import Image from "next/image";
import { useState } from "react";
interface Feature {
icon: LucideIcon;
title: string;
description: string;
}
interface Tab {
id: string;
label: string;
icon: LucideIcon;
title: string;
description: string;
features: Feature[];
image?: string;
cta?: {
text: string;
href?: string;
onClick?: () => void;
};
}
interface FeatureTabsProps {
badge?: string;
headline: string;
description?: string;
tabs: Tab[];
}
export function FeatureTabs({
badge,
headline,
description,
tabs,
}: FeatureTabsProps) {
const [activeTab, setActiveTab] = useState(tabs[0]?.id || "");
const colors = [
{ from: "from-orange-500", to: "to-amber-500", bg: "bg-orange-500" },
{ from: "from-blue-500", to: "to-cyan-500", bg: "bg-blue-500" },
{ from: "from-purple-500", to: "to-pink-500", bg: "bg-purple-500" },
{ from: "from-green-500", to: "to-emerald-500", bg: "bg-green-500" },
];
return (
<section className="relative overflow-hidden py-24 sm:py-32">
<div className="absolute inset-0 -z-10">
<div className="absolute left-0 top-1/2 -z-10 h-[500px] w-[500px] -translate-y-1/2 rounded-full bg-gradient-to-r from-blue-500/10 to-transparent blur-3xl" />
<div className="absolute right-0 top-1/4 -z-10 h-[400px] w-[400px] rounded-full bg-gradient-to-l from-purple-500/10 to-transparent blur-3xl" />
</div>
<div className="container mx-auto px-6">
<div className="mx-auto mb-16 max-w-3xl text-center">
{badge && (
<div className="mb-6 inline-flex animate-in fade-in slide-in-from-bottom-3 duration-700">
<Badge
variant="outline"
className="border-primary/20 bg-primary/10 px-4 py-1.5 text-sm font-medium text-primary backdrop-blur-sm"
>
<span className="relative mr-2 flex h-2 w-2">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75"></span>
<span className="relative inline-flex h-2 w-2 rounded-full bg-primary"></span>
</span>
{badge}
</Badge>
</div>
)}
<h2 className="mb-6 animate-in fade-in slide-in-from-bottom-4 text-4xl font-bold tracking-tight duration-1000 sm:text-5xl md:text-6xl">
{headline}
</h2>
{description && (
<p className="animate-in fade-in slide-in-from-bottom-5 text-lg leading-relaxed text-muted-foreground duration-1000 sm:text-xl">
{description}
</p>
)}
</div>
<div className="animate-in fade-in slide-in-from-bottom-6 duration-1000">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="w-full"
>
<TabsList className="mb-12 grid h-auto w-full grid-cols-2 gap-4 bg-transparent p-0 lg:grid-cols-4">
{tabs.map((tab, index) => {
const Icon = tab.icon;
const color = colors[index % colors.length];
const isActive = activeTab === tab.id;
return (
<TabsTrigger
key={tab.id}
value={tab.id}
className="group relative h-auto overflow-hidden rounded-xl border border-border/50 bg-card/50 p-6 backdrop-blur-sm transition-all duration-300 data-[state=active]:border-primary/50 data-[state=active]:bg-card data-[state=active]:shadow-xl hover:border-border hover:bg-card hover:shadow-lg"
>
<div
className={`absolute -inset-0.5 rounded-xl bg-gradient-to-r ${color.from} ${color.to} opacity-0 blur transition-all duration-500 group-data-[state=active]:opacity-20`}
/>
<div className="relative flex flex-col items-center gap-3">
<div
className={`rounded-xl bg-gradient-to-br ${color.from} ${color.to} p-3 shadow-lg transition-all duration-300 group-data-[state=active]:scale-110 group-data-[state=active]:shadow-xl group-hover:scale-105`}
>
<Icon className="h-5 w-5 text-white" />
</div>
<span className="text-sm font-semibold transition-colors group-data-[state=active]:text-foreground">
{tab.label}
</span>
</div>
<div
className={`absolute bottom-0 left-0 h-1 w-0 rounded-full bg-gradient-to-r ${color.from} ${color.to} transition-all duration-500 group-data-[state=active]:w-full`}
/>
</TabsTrigger>
);
})}
</TabsList>
{tabs.map((tab, tabIndex) => {
const currentTab = tabs.find((t) => t.id === activeTab);
const color = colors[tabIndex % colors.length];
return (
<TabsContent
key={tab.id}
value={tab.id}
className="mt-0 animate-in fade-in zoom-in-95 duration-500"
>
<Card className="overflow-hidden border-border/50 bg-card/50 backdrop-blur-sm">
<CardContent className="p-8 sm:p-12">
<div className="grid gap-12 lg:grid-cols-2 lg:gap-16">
<div className="flex flex-col justify-center">
<div className="mb-6 inline-flex">
<div
className={`rounded-xl bg-gradient-to-br ${color.from} ${color.to} p-3 shadow-lg`}
>
<tab.icon className="h-8 w-8 text-white" />
</div>
</div>
<h3 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">
{tab.title}
</h3>
<p className="mb-8 text-lg leading-relaxed text-muted-foreground">
{tab.description}
</p>
<div className="mb-8 space-y-3">
{tab.features.map((feature, index) => {
const FeatureIcon = feature.icon;
return (
<div
key={index}
className="group flex items-start gap-4 rounded-xl border border-border/50 bg-background/50 p-4 transition-all duration-300 hover:border-border hover:bg-background hover:shadow-lg"
style={{
animationDelay: `${index * 100}ms`,
}}
>
<div
className={`mt-0.5 rounded-lg bg-gradient-to-br ${color.from} ${color.to} p-2 shadow-md transition-transform duration-300 group-hover:scale-110`}
>
<FeatureIcon className="h-4 w-4 text-white" />
</div>
<div className="flex-1">
<h4 className="mb-1 font-semibold">
{feature.title}
</h4>
<p className="text-sm leading-relaxed text-muted-foreground">
{feature.description}
</p>
</div>
<CheckCircle2 className="mt-1 h-5 w-5 flex-shrink-0 text-green-500" />
</div>
);
})}
</div>
{tab.cta && (
<div>
<Button
size="lg"
onClick={tab.cta.onClick}
className={`group bg-gradient-to-r ${color.from} ${color.to} text-white shadow-lg transition-all hover:scale-105 hover:shadow-xl`}
>
{tab.cta.text}
<ArrowRight className="ml-2 h-4 w-4 transition-transform group-hover:translate-x-1" />
</Button>
</div>
)}
</div>
<div className="relative">
<div className="group relative aspect-[4/3] overflow-hidden rounded-2xl border border-border/50 bg-gradient-to-br from-muted/50 to-muted shadow-2xl transition-all duration-500 hover:shadow-3xl">
{tab.image ? (
<Image
src={tab.image}
alt={tab.title}
fill
className="object-cover transition-transform duration-700 group-hover:scale-105"
/>
) : (
<div className="flex h-full items-center justify-center p-8">
<div className="text-center">
<div
className={`mx-auto mb-6 inline-flex h-24 w-24 items-center justify-center rounded-2xl bg-gradient-to-br ${color.from} ${color.to} shadow-2xl transition-transform duration-500 group-hover:scale-110 group-hover:rotate-3`}
>
{currentTab && (
<currentTab.icon className="h-12 w-12 text-white" />
)}
</div>
<p className="text-sm font-medium text-muted-foreground">
Feature Preview
</p>
</div>
</div>
)}
<div
className={`absolute inset-0 bg-gradient-to-tr ${color.from}/10 via-transparent to-purple-500/10 opacity-0 transition-opacity duration-500 group-hover:opacity-100`}
/>
</div>
<div
className={`absolute -right-4 -top-4 h-24 w-24 rounded-full bg-gradient-to-br ${color.from} ${color.to} opacity-20 blur-2xl transition-all duration-700 group-hover:opacity-30`}
/>
<div
className={`absolute -bottom-4 -left-4 h-32 w-32 rounded-full bg-gradient-to-tr ${color.from} ${color.to} opacity-20 blur-2xl transition-all duration-700 group-hover:opacity-30`}
/>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
</section>
);
}Update the import paths to match your project setup.
Usage
Basic Usage
import FeatureTabs from "@/components/ui/feature-tabs";
import { Zap, Shield, Rocket, Code } from "lucide-react";
export default function FeaturesPage() {
return (
<FeatureTabs
headline="Powerful features"
tabs={[
{
id: "performance",
label: "Performance",
icon: Zap,
title: "Lightning fast",
description: "Built for speed from the ground up.",
features: [
{
icon: Zap,
title: "Edge Computing",
description: "Deploy to 300+ locations worldwide.",
},
],
},
]}
/>
);
}Full Example with All Features
import FeatureTabs from "@/components/ui/feature-tabs";
import { Zap, Shield, Rocket, Code } from "lucide-react";
export default function FeaturesPage() {
return (
<FeatureTabs
badge="Platform Features"
headline="Everything you need in one platform"
description="Powerful features organized by category. Switch between tabs to explore."
tabs={[
{
id: "performance",
label: "Performance",
icon: Zap,
title: "Lightning-fast performance",
description:
"Built for speed with edge computing and intelligent caching.",
features: [
{
icon: Zap,
title: "Edge Computing",
description: "Deploy to 300+ edge locations worldwide.",
},
{
icon: Cpu,
title: "Smart Caching",
description: "Reduce load times by 10x with intelligent caching.",
},
],
image: "/features/performance.png", // Optional
cta: {
text: "Learn More",
onClick: () => console.log("CTA clicked"),
},
},
{
id: "security",
label: "Security",
icon: Shield,
title: "Enterprise-grade security",
description: "Your data is protected with industry-leading security.",
features: [
{
icon: Lock,
title: "End-to-End Encryption",
description: "All data encrypted with AES-256.",
},
],
cta: {
text: "View Security",
href: "/security",
},
},
]}
/>
);
}With Router Navigation
"use client";
import { useRouter } from "next/navigation";
import FeatureTabs from "@/components/ui/feature-tabs";
export default function FeaturesPage() {
const router = useRouter();
return (
<FeatureTabs
headline="Explore our features"
tabs={[
{
id: "tab1",
label: "Features",
icon: Zap,
title: "Amazing features",
description: "Everything you need.",
features: [
/* ... */
],
cta: {
text: "Get Started",
onClick: () => router.push("/signup"),
},
},
]}
/>
);
}Props
FeatureTabsProps
| Prop | Type | Default | Description |
|---|---|---|---|
badge | string | undefined | Optional badge text above headline |
headline | string | Required | Main section headline |
description | string | undefined | Optional description below headline |
tabs | Tab[] | Required | Array of tab objects |
Tab Object
| Prop | Type | Description |
|---|---|---|
id | string | Unique identifier for the tab |
label | string | Tab label text |
icon | LucideIcon | Icon component for the tab |
title | string | Title shown in tab content |
description | string | Description shown in tab content |
features | Feature[] | Array of feature objects |
image | string | Optional image URL |
cta | object | Optional call-to-action button |
cta.text | string | Button text |
cta.href | string | Optional link URL |
cta.onClick | () => void | Optional click handler |
Feature Object
| Prop | Type | Description |
|---|---|---|
icon | LucideIcon | Icon component |
title | string | Feature title |
description | string | Feature description |
TypeScript Interface
import { LucideIcon } from "lucide-react";
interface Feature {
icon: LucideIcon;
title: string;
description: string;
}
interface Tab {
id: string;
label: string;
icon: LucideIcon;
title: string;
description: string;
features: Feature[];
image?: string;
cta?: {
text: string;
href?: string;
onClick?: () => void;
};
}
interface FeatureTabsProps {
badge?: string;
headline: string;
description?: string;
tabs: Tab[];
}Customization
Change Tab Layout
Adjust the grid columns for tab triggers:
{/* 3 columns on desktop */}
<TabsList className="grid-cols-2 lg:grid-cols-3">
{/* 5 columns on desktop */}
<TabsList className="grid-cols-2 lg:grid-cols-5">Customize Tab Colors
The component uses a predefined color scheme. To customize:
const colors = [
{ from: "from-red-500", to: "to-rose-500", bg: "bg-red-500" },
{ from: "from-green-500", to: "to-emerald-500", bg: "bg-green-500" },
{ from: "from-yellow-500", to: "to-orange-500", bg: "bg-yellow-500" },
{ from: "from-indigo-500", to: "to-violet-500", bg: "bg-indigo-500" },
];Customize Tab Trigger Style
<TabsTrigger
className="rounded-xl border-2 p-8 data-[state=active]:border-blue-500 data-[state=active]:bg-blue-50"
>
{/* content */}
</TabsTrigger>Add Custom Images
tabs={[
{
id: "performance",
label: "Performance",
icon: Zap,
title: "Fast performance",
description: "Lightning fast.",
features: [/* ... */],
image: "/images/performance-dashboard.png", // Add your image
},
]}Remove Images
Simply omit the image property and a placeholder will be shown:
tabs={[
{
id: "performance",
// ... other props
// No image property - placeholder will show
},
]}Use Cases
Perfect for:
- Product feature pages
- Platform capabilities
- Service offerings
- Tool comparisons
- Multi-product showcases
- Category-based features
- Technical specifications
- Integration showcases
Best Practices
- Limit tabs - 3-5 tabs work best for usability
- Consistent features - Keep similar number of features per tab (3-4 recommended)
- Clear labels - Use short, descriptive tab labels (1-2 words)
- Relevant icons - Choose icons that clearly represent each category
- Concise descriptions - Keep feature descriptions to 1-2 sentences
- Logical grouping - Group related features together
- Mobile testing - Test tab switching on mobile devices
- Color harmony - The default colors work well together, customize carefully