shadcn/ui Accordion is built on Radix UI and provides fully accessible expand/collapse panels. These 4 examples cover FAQ, multi-open, custom styled and settings panel patterns.
Installation
npx shadcn@latest add accordion
Usage
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
// Single open (collapsible)
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Question?</AccordionTrigger>
<AccordionContent>Answer.</AccordionContent>
</AccordionItem>
</Accordion>
// Multiple open simultaneously
<Accordion type="multiple">
1. Basic shadcn/ui Accordion (FAQ)
Standard FAQ accordion — only one item open at a time. Uses shadcn/ui Accordion with type='single'.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
const faqs = [
{
value: "q1",
question: "What frameworks does BootstrapPlanet cover?",
answer: "Bootstrap 5, React with Tailwind CSS and shadcn/ui, and Angular 21 with Bootstrap 5. Over 200 free components with copy-paste code and live previews.",
},
{
value: "q2",
question: "Are all the components free to use?",
answer: "Yes. Everything on BootstrapPlanet is completely free. No signup, no paywall. Just copy the code and use it in your project.",
},
{
value: "q3",
question: "Do you have premium templates?",
answer: "Yes. Full Angular 21 + Bootstrap 5 and React + Next.js admin dashboard templates are available at LettStartDesign.com. Use code FIRST30 for 30% off your first purchase.",
},
{
value: "q4",
question: "How often are components updated?",
answer: "We publish new components and update existing ones regularly. Bootstrap components track Bootstrap 5.3, React components track the latest shadcn/ui releases, and Angular components track Angular 21.",
},
{
value: "q5",
question: "Can I use these components in commercial projects?",
answer: "Yes. All code on BootstrapPlanet is free to use in any project — personal, commercial, open source or closed source. No attribution required.",
},
]
export default function BasicAccordion() {
return (
<div className="max-w-xl">
<h2 className="text-lg font-bold mb-4">Frequently Asked Questions</h2>
<Accordion type="single" collapsible className="w-full">
{faqs.map((faq) => (
<AccordionItem key={faq.value} value={faq.value}>
<AccordionTrigger className="text-left">{faq.question}</AccordionTrigger>
<AccordionContent className="text-muted-foreground">
{faq.answer}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}2. Always-Open Accordion (Multiple Items)
Accordion that allows multiple panels open simultaneously using type='multiple'.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import { Check } from "lucide-react"
const features = [
{
value: "f1",
title: "Bootstrap 5 Components",
description: "80+ free Bootstrap 5 components with live previews.",
items: ["Navbars", "Cards", "Forms", "Tables", "Modals", "Layouts"],
defaultOpen: true,
},
{
value: "f2",
title: "React + shadcn/ui Components",
description: "40 React components built with Tailwind CSS and shadcn/ui.",
items: ["Buttons", "Modals", "Login Forms", "Data Tables", "Sidebars"],
defaultOpen: true,
},
{
value: "f3",
title: "Angular + Bootstrap Components",
description: "Angular 21 standalone components with Bootstrap 5 styling.",
items: ["Navbar", "Sidebar", "Forms", "Data Table", "Dark Theme"],
defaultOpen: false,
},
]
export default function MultipleAccordion() {
return (
<div className="max-w-xl">
<h2 className="text-lg font-bold mb-4">What's Included</h2>
<Accordion
type="multiple"
defaultValue={features.filter(f => f.defaultOpen).map(f => f.value)}
className="w-full"
>
{features.map((feature) => (
<AccordionItem key={feature.value} value={feature.value}>
<AccordionTrigger>{feature.title}</AccordionTrigger>
<AccordionContent>
<p className="text-muted-foreground text-sm mb-3">{feature.description}</p>
<ul className="space-y-1.5">
{feature.items.map((item) => (
<li key={item} className="flex items-center gap-2 text-sm">
<Check size={14} className="text-green-500 flex-shrink-0" />
{item}
</li>
))}
</ul>
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}3. Custom Styled Accordion
Accordion with brand colors, custom trigger styles and icon rotation animation.
import { useState } from "react"
import { ChevronDown } from "lucide-react"
const items = [
{
id: 1,
title: "Angular 21 + Bootstrap 5 Templates",
content: "Our flagship Marvel Angular Dashboard includes 50+ components, 4 layout variants, dark mode and RTL support. Built on Angular 21 standalone components with Bootstrap 5.3.",
},
{
id: 2,
title: "React + Next.js Templates",
content: "Proctu Medical and Kiosk Dashboard templates are built on React 19 and Next.js 15 App Router. Includes TypeScript, Tailwind CSS and shadcn/ui components.",
},
{
id: 3,
title: "Bootstrap 5 Templates",
content: "PORTO Bootstrap 5 template — 152 sales, 5.0 rating. Clean multipurpose design with SCSS source files, multiple layouts and detailed documentation.",
},
{
id: 4,
title: "Free vs Premium Templates",
content: "Free templates include basic layouts with limited components. Premium templates include all components, dark mode, RTL support, full source code and ongoing updates.",
},
]
export default function CustomAccordion() {
const [openId, setOpenId] = useState<number | null>(1)
return (
<div className="max-w-xl space-y-2">
{items.map((item) => {
const isOpen = openId === item.id
return (
<div
key={item.id}
className="border rounded-xl overflow-hidden transition-all"
style={{ borderColor: isOpen ? "#fd4766" : "#e5e7eb" }}
>
<button
className="w-full flex items-center justify-between px-4 py-3.5 text-left transition-colors"
style={{
background: isOpen ? "rgba(253,71,102,0.04)" : "#fff",
}}
onClick={() => setOpenId(isOpen ? null : item.id)}
>
<span
className="font-semibold text-sm"
style={{ color: isOpen ? "#fd4766" : "#111827" }}
>
{item.title}
</span>
<ChevronDown
size={18}
className="flex-shrink-0 transition-transform duration-200"
style={{
color: isOpen ? "#fd4766" : "#9ca3af",
transform: isOpen ? "rotate(180deg)" : "rotate(0deg)",
}}
/>
</button>
<div
className="overflow-hidden transition-all duration-300"
style={{ maxHeight: isOpen ? "200px" : "0px" }}
>
<div className="px-4 pb-4 pt-1">
<p className="text-sm text-gray-600 leading-relaxed">{item.content}</p>
<a
href="https://lettstartdesign.com"
className="inline-flex items-center gap-1 text-xs font-semibold mt-2"
style={{ color: "#fd4766" }}
target="_blank"
rel="noopener"
>
View Templates → Use code FIRST30 for 30% off
</a>
</div>
</div>
</div>
)
})}
</div>
)
}4. Settings Accordion Panels
Accordion used for settings sections — common pattern in admin dashboards and account pages.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Button } from "@/components/ui/button"
export default function SettingsAccordion() {
return (
<div className="max-w-xl">
<h2 className="text-lg font-bold mb-4">Account Settings</h2>
<Accordion type="single" collapsible defaultValue="profile" className="w-full">
<AccordionItem value="profile">
<AccordionTrigger>
<div className="flex items-center gap-2">
<span>👤</span> Profile Information
</div>
</AccordionTrigger>
<AccordionContent>
<div className="space-y-3 pt-1">
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1">
<Label className="text-xs">First Name</Label>
<Input defaultValue="Gagan" />
</div>
<div className="space-y-1">
<Label className="text-xs">Last Name</Label>
<Input defaultValue="Singh" />
</div>
</div>
<div className="space-y-1">
<Label className="text-xs">Email</Label>
<Input defaultValue="gagan@lettstartdesign.com" type="email" />
</div>
<Button size="sm" style={{ background: "#fd4766" }}>Save Profile</Button>
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="password">
<AccordionTrigger>
<div className="flex items-center gap-2">
<span>🔒</span> Change Password
</div>
</AccordionTrigger>
<AccordionContent>
<div className="space-y-3 pt-1">
<div className="space-y-1">
<Label className="text-xs">Current Password</Label>
<Input type="password" placeholder="••••••••" />
</div>
<div className="space-y-1">
<Label className="text-xs">New Password</Label>
<Input type="password" placeholder="••••••••" />
</div>
<Button size="sm" variant="outline">Update Password</Button>
</div>
</AccordionContent>
</AccordionItem>
<AccordionItem value="notifications">
<AccordionTrigger>
<div className="flex items-center gap-2">
<span>🔔</span> Notifications
</div>
</AccordionTrigger>
<AccordionContent>
<div className="space-y-3 pt-1">
{["Email notifications", "Push notifications", "Weekly digest", "Marketing emails"].map((item) => (
<div key={item} className="flex items-center justify-between">
<span className="text-sm">{item}</span>
<input type="checkbox" defaultChecked={item.includes("Email")} className="w-4 h-4 accent-[#fd4766]" />
</div>
))}
<Button size="sm" style={{ background: "#fd4766" }}>Save Preferences</Button>
</div>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
)
}Frequently Asked Questions
Need a Full React + Next.js Dashboard Template?
Get a complete React + Next.js dashboard with 50+ components — built by the same team behind BootstrapPlanet.
Browse Templates →Use code FIRST30 for 30% off.