UI/UI
Accordion
A vertically stacked set of interactive headings that each reveal an associated section of content.
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
export function AccordionDemo() {
return (
<Accordion type="single" collapsible className="w-full max-w-2xl mx-auto">
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern and includes full keyboard
navigation support.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>Is it styled?</AccordionTrigger>
<AccordionContent>
Yes. It comes with default styles that match the design system, but you
can customize them to fit your needs.
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionTrigger>Is it animated?</AccordionTrigger>
<AccordionContent>
Yes. It uses smooth CSS animations for expanding and collapsing that
enhance the user experience.
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
Installation
Install following dependencies:
npm install @radix-ui/react-accordion motion class-variance-authority lucide-react
pnpm add @radix-ui/react-accordion motion class-variance-authority lucide-react
yarn add @radix-ui/react-accordion motion class-variance-authority lucide-react
bun add @radix-ui/react-accordion motion class-variance-authority lucide-react
Add required animation and keyframes to your CSS file or tailwind config file based on your Tailwind version.
@theme {
--animate-accordion-down: accordion-down 0.3s ease-out;
--animate-accordion-up: accordion-up 0.3s ease-out;
}
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
module.exports = {
theme: {
extend: {
keyframes: {
accordion-down: {
from: {
height: 0,
},
to: {
height: var(--radix-accordion-content-height),
},
},
accordion-up: {
from: {
height: var(--radix-accordion-content-height),
},
to: {
height: 0,
},
},
}
animations: {
"accordion-down": "accordion-down 0.3s ease-out",
"accordion-up": "accordion-up 0.3s ease-out",
},
}
}
}
Copy and paste the following code into your project.
"use client";
import * as React from "react";
import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { ChevronDown } from "lucide-react";
const accordionVariants = cva("w-full max-w-ele", {
variants: {
variant: {
default:
"border border-border rounded-ele overflow-hidden shadow-sm/2",
ghost: "",
outline:
"border border-border rounded-ele shadow-sm/2",
},
size: {
sm: "text-sm max-w-lg",
default: "max-w-2xl",
lg: "text-lg max-w-4xl",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
const accordionItemVariants = cva(
"border-b border-border last:border-b-0",
{
variants: {
variant: {
default: "",
ghost:
"border-b border-border last:border-b-0 mb-2 last:mb-0",
outline: "",
},
},
defaultVariants: {
variant: "default",
},
}
);
const accordionTriggerVariants = cva(
"flex flex-1 items-center justify-between py-4 px-6 text-left font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group",
{
variants: {
variant: {
default: "",
ghost: "px-0",
outline: "",
},
size: {
sm: "py-3 px-4 text-sm",
default: "py-4 px-6",
lg: "py-5 px-6 text-lg",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
const accordionContentVariants = cva(
"px-6 pb-4 pt-0 text-muted-foreground",
{
variants: {
variant: {
default: "",
ghost: "px-0",
outline: "",
},
size: {
sm: "px-4 pb-3 text-sm",
default: "px-6 pb-4",
lg: "px-6 pb-5",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
);
export interface AccordionProps extends VariantProps<typeof accordionVariants> {
className?: string;
children?: React.ReactNode;
}
// Single accordion props
export interface AccordionSingleProps extends AccordionProps {
type: "single";
collapsible?: boolean;
defaultValue?: string;
value?: string;
onValueChange?: (value: string) => void;
}
// Multiple accordion props
export interface AccordionMultipleProps extends AccordionProps {
type: "multiple";
defaultValue?: string[];
value?: string[];
onValueChange?: (value: string[]) => void;
}
export type AccordionCombinedProps =
| AccordionSingleProps
| AccordionMultipleProps;
export interface AccordionItemProps
extends VariantProps<typeof accordionItemVariants> {
value: string;
disabled?: boolean;
className?: string;
children?: React.ReactNode;
}
export interface AccordionTriggerProps
extends VariantProps<typeof accordionTriggerVariants> {
icon?: React.ReactNode;
hideChevron?: boolean;
className?: string;
children?: React.ReactNode;
onClick?: () => void;
}
export interface AccordionContentProps
extends VariantProps<typeof accordionContentVariants> {
className?: string;
children?: React.ReactNode;
}
const Accordion = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Root>,
AccordionCombinedProps
>(({ className, variant, size, children, ...props }, ref) => (
<AccordionPrimitive.Root
ref={ref}
className={cn(accordionVariants({ variant, size }), className)}
{...props}
>
{children}
</AccordionPrimitive.Root>
));
Accordion.displayName = "Accordion";
const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
AccordionItemProps
>(({ className, variant, children, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn(accordionItemVariants({ variant }), className)}
{...props}
>
{children}
</AccordionPrimitive.Item>
));
AccordionItem.displayName = "AccordionItem";
const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
AccordionTriggerProps
>(
(
{ className, children, variant, size, icon, hideChevron = false, ...props },
ref
) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(accordionTriggerVariants({ variant, size }), className)}
{...props}
>
<div className="flex items-center gap-2">
{icon && <span className="shrink-0">{icon}</span>}
<span className="text-left group-hover:underline transition-all duration-200">
{children}
</span>
</div>
{!hideChevron && (
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200 ease-in-out group-data-[state=open]:rotate-180" />
)}
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
);
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
AccordionContentProps
>(({ className, children, variant, size, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn(accordionContentVariants({ variant, size }), className)}>
{children}
</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
npx hextaui@latest add accordion
pnpm dlx hextaui@latest add accordion
yarn dlx hextaui@latest add accordion
bun x hextaui@latest add accordion
Usage
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Is it accessible?</AccordionTrigger>
<AccordionContent>
Yes. It adheres to the WAI-ARIA design pattern.
</AccordionContent>
</AccordionItem>
</Accordion>
Examples
Multiple Items
<Accordion type="multiple" className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger>Can I open multiple items?</AccordionTrigger>
<AccordionContent>
Yes. You can configure the accordion to allow multiple items to be
open at the same time by setting type="multiple".
</AccordionContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionTrigger>How does it work?</AccordionTrigger>
<AccordionContent>
When type is set to "multiple", users can expand multiple accordion
items simultaneously.
</AccordionContent>
</AccordionItem>
</Accordion>
Variants
Default
Ghost
Outline
{/* Default */}
<Accordion type="single" collapsible variant="default">
<AccordionItem value="item-1">
<AccordionTrigger>Default variant</AccordionTrigger>
<AccordionContent>
This is the default accordion variant with borders and background.
</AccordionContent>
</AccordionItem>
</Accordion>
{/* Ghost */}
<Accordion type="single" collapsible variant="ghost">
<AccordionItem value="item-1" variant="ghost">
<AccordionTrigger variant="ghost">Ghost variant</AccordionTrigger>
<AccordionContent variant="ghost">
This is the ghost accordion variant with minimal styling.
</AccordionContent>
</AccordionItem>
</Accordion>
{/* Outline */}
<Accordion type="single" collapsible variant="outline">
<AccordionItem value="item-1">
<AccordionTrigger variant="outline">Outline variant</AccordionTrigger>
<AccordionContent variant="outline">
This is the outline accordion variant with border styling.
</AccordionContent>
</AccordionItem>
</Accordion>
Sizes
Small
Default
Large
{/* Small */}
<Accordion type="single" collapsible size="sm">
<AccordionItem value="item-1">
<AccordionTrigger size="sm">Small accordion</AccordionTrigger>
<AccordionContent size="sm">
This is a small-sized accordion with compact spacing.
</AccordionContent>
</AccordionItem>
</Accordion>
{/* Default */}
<Accordion type="single" collapsible>
<AccordionItem value="item-1">
<AccordionTrigger>Default accordion</AccordionTrigger>
<AccordionContent>
This is the default-sized accordion with standard spacing.
</AccordionContent>
</AccordionItem>
</Accordion>
{/* Large */}
<Accordion type="single" collapsible size="lg">
<AccordionItem value="item-1">
<AccordionTrigger size="lg">Large accordion</AccordionTrigger>
<AccordionContent size="lg">
This is a large-sized accordion with generous spacing.
</AccordionContent>
</AccordionItem>
</Accordion>
With Icons
import { Star, Shield, Zap, Heart } from "lucide-react";
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="features">
<AccordionTrigger icon={<Star className="h-4 w-4" />}>
Features
</AccordionTrigger>
<AccordionContent>
Our platform includes advanced features like real-time collaboration,
version control, and automated deployments.
</AccordionContent>
</AccordionItem>
<AccordionItem value="security">
<AccordionTrigger icon={<Shield className="h-4 w-4" />}>
Security
</AccordionTrigger>
<AccordionContent>
We implement enterprise-grade security with end-to-end encryption,
two-factor authentication, and regular security audits.
</AccordionContent>
</AccordionItem>
</Accordion>
Custom Styling
Rounded
No Chevron
Custom Icon
import { ChevronRight } from "lucide-react";
<div className="space-y-6 w-full max-w-2xl mx-auto">
<div>
<h3 className="text-lg font-semibold mb-3">Rounded</h3>
<Accordion
type="single"
collapsible
className="w-full rounded-ele overflow-hidden"
>
<AccordionItem value="item-1">
<AccordionTrigger>Rounded accordion</AccordionTrigger>
<AccordionContent>
This accordion has custom rounded corners applied.
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div>
<h3 className="text-lg font-semibold mb-3">No Chevron</h3>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger hideChevron>
Accordion without chevron
</AccordionTrigger>
<AccordionContent>
This accordion item has the chevron icon hidden.
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
<div>
<h3 className="text-lg font-semibold mb-3">Custom Icon</h3>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger
icon={<ChevronRight className="h-4 w-4" />}
hideChevron
>
Custom expand icon
</AccordionTrigger>
<AccordionContent>
This accordion uses a custom icon instead of the default chevron.
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
</div>
FAQ Example
import { HelpCircle } from "lucide-react";
const faqs = [
{
question: "How do I get started?",
answer: "Getting started is easy! Simply sign up for an account, choose your plan, and follow our step-by-step onboarding guide."
},
{
question: "What payment methods do you accept?",
answer: "We accept all major credit cards (Visa, MasterCard, American Express), PayPal, and bank transfers for enterprise customers."
},
// ... more FAQs
];
<div className="max-w-2xl mx-auto">
<Accordion type="single" collapsible className="w-full">
{faqs.map((faq, index) => (
<AccordionItem key={index} value={`faq-${index}`}>
<AccordionTrigger icon={<HelpCircle className="h-4 w-4" />}>
{faq.question}
</AccordionTrigger>
<AccordionContent>
{faq.answer}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
Nested Accordions
import { User, Settings } from "lucide-react";
<div className="w-full max-w-2xl mx-auto">
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="account">
<AccordionTrigger icon={<User className="h-4 w-4" />}>
Account Settings
</AccordionTrigger>
<AccordionContent>
<Accordion
type="single"
collapsible
variant="ghost"
className="mt-2 w-full"
>
<AccordionItem value="profile" variant="ghost">
<AccordionTrigger variant="ghost" size="sm">
Profile Information
</AccordionTrigger>
<AccordionContent variant="ghost" size="sm">
Manage your personal information, profile picture, and bio.
</AccordionContent>
</AccordionItem>
<AccordionItem value="privacy" variant="ghost">
<AccordionTrigger variant="ghost" size="sm">
Privacy Settings
</AccordionTrigger>
<AccordionContent variant="ghost" size="sm">
Control who can see your profile and contact you.
</AccordionContent>
</AccordionItem>
</Accordion>
</AccordionContent>
</AccordionItem>
<AccordionItem value="preferences">
<AccordionTrigger icon={<Settings className="h-4 w-4" />}>
Preferences
</AccordionTrigger>
<AccordionContent>
<Accordion
type="single"
collapsible
variant="ghost"
className="mt-2 w-full"
>
<AccordionItem value="notifications" variant="ghost">
<AccordionTrigger variant="ghost" size="sm">
Notifications
</AccordionTrigger>
<AccordionContent variant="ghost" size="sm">
Configure email and push notification preferences.
</AccordionContent>
</AccordionItem>
<AccordionItem value="theme" variant="ghost">
<AccordionTrigger variant="ghost" size="sm">
Theme & Appearance
</AccordionTrigger>
<AccordionContent variant="ghost" size="sm">
Choose between light, dark, or system theme preferences.
</AccordionContent>
</AccordionItem>
</Accordion>
</AccordionContent>
</AccordionItem>
</Accordion>
</div>
Props
Accordion
Prop | Type | Default |
---|---|---|
className? | string | undefined |
size? | "sm" | "default" | "lg" | "default" |
variant? | "default" | "ghost" | "outline" | "default" |
onValueChange? | function | undefined |
valueArray? | string[] | undefined |
value? | string | undefined |
defaultValueArray? | string[] | undefined |
defaultValue? | string | undefined |
collapsible? | boolean | false |
type? | "single" | "multiple" | undefined |
AccordionItem
Prop | Type | Default |
---|---|---|
className? | string | undefined |
variant? | "default" | "ghost" | "outline" | "default" |
disabled? | boolean | false |
value? | string | undefined |
AccordionTrigger
Prop | Type | Default |
---|---|---|
children? | ReactNode | undefined |
className? | string | undefined |
size? | "sm" | "default" | "lg" | "default" |
variant? | "default" | "ghost" | "outline" | "default" |
hideChevron? | boolean | false |
icon? | ReactNode | undefined |
AccordionContent
Prop | Type | Default |
---|---|---|
children? | ReactNode | undefined |
className? | string | undefined |
size? | "sm" | "default" | "lg" | "default" |
variant? | "default" | "ghost" | "outline" | "default" |
Edit on GitHub
Last updated on