Dependencies
tailwindcssshadcn/ui
shadcn/ui components
npx shadcn@latest add accordion

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'.

tsx
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'.

tsx
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&apos;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.

tsx
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.

tsx
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

Run npx shadcn@latest add accordion. This creates components/ui/accordion.tsx. Import with: import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion'.
Set type='multiple' on the Accordion component: <Accordion type='multiple'>. Set defaultValue as an array of values: defaultValue={['item-1', 'item-2']}. With type='single' only one item can be open at a time.
Use the defaultValue prop: <Accordion type='single' defaultValue='item-1'>. For controlled accordion use value and onValueChange props instead of defaultValue.
The AccordionItem has a border-b by default. Override with className: <AccordionItem className='border-0' value='item-1'>. Or edit accordion.tsx directly to remove the border from the base styles.

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.

Related Components