Layout
Accordion
Expandable content panels for FAQs, settings, and similar use cases. Built on Base UI Accordion with single and multiple open modes.
Installation
bash
npx polyui add accordionBasic
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionBasic() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full">
{faqItems.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger>{item.title}</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Split
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionSplit() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full space-y-2">
{faqItems.map((item) => (
<AccordionItem
key={item.value}
value={item.value}
className="bg-card rounded-md border-b-0 shadow-md data-open:shadow-lg"
>
<AccordionTrigger className="px-5">{item.title}</AccordionTrigger>
<AccordionContent className="text-muted-foreground px-5">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}With Icon
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionWithIcon() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full">
{iconItems.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger>
<span className="flex items-center gap-4">
<item.icon className="text-muted-foreground size-4 shrink-0" />
<span>{item.title}</span>
</span>
</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Plus Icon
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { PlusIcon } from "lucide-react"
export function AccordionPlusIcon() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{iconItems.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<div className="flex">
<AccordionTrigger
data-slot="accordion-trigger"
className="focus-visible:border-ring focus-visible:ring-ring/50 group/accordion-trigger flex flex-1 items-start justify-between gap-4 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
>
<span className="flex items-center gap-4">
<item.icon className="size-4 shrink-0" />
<span>{item.title}</span>
</span>
<PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
</AccordionTrigger>
</div>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Icon & Subtitle
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { PlusIcon } from "lucide-react"
export function AccordionIconSubtitle() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{iconItems.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<div className="flex">
<AccordionTrigger
data-slot="accordion-trigger"
className="focus-visible:border-ring focus-visible:ring-ring/50 group/accordion-trigger flex flex-1 items-center justify-between gap-4 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
>
<span className="flex items-center gap-4">
<span
className="flex size-10 shrink-0 items-center justify-center rounded-full border"
aria-hidden="true"
>
<item.icon className="size-4" />
</span>
<span className="flex flex-col space-y-0.5">
<span>{item.title}</span>
<span className="text-muted-foreground font-normal">{item.subtitle}</span>
</span>
</span>
<PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
</AccordionTrigger>
</div>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Active Highlight
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionActiveHighlight() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{faqItems.map((item) => (
<AccordionItem
key={item.value}
value={item.value}
className="data-open:border-amber-600 not-last:data-open:border-b-2 dark:data-open:border-amber-400"
>
<AccordionTrigger className="data-open:text-amber-600 dark:data-open:text-amber-400">
{item.title}
</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}With Avatar
Richard Payne is a remarkable individual known for his exceptional skills and expertise in various fields.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { Avatar, AvatarImage, AvatarFallback } from "@polyui/react/avatar"
export function AccordionWithAvatar() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{avatarItems.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger className="items-center hover:no-underline">
<span className="flex items-center gap-4">
<Avatar className="size-10 rounded-sm">
<AvatarImage src={item.src} alt={item.name} className="rounded-sm" />
<AvatarFallback className="rounded-sm text-xs">
{item.name.split(/\s/).reduce((acc, word) => acc + word.slice(0, 1), "")}
</AvatarFallback>
</Avatar>
<span className="flex flex-col space-y-0.5">
<span>{item.name}</span>
<span className="text-muted-foreground font-normal">{item.email}</span>
</span>
</span>
</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Outline Separated
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionOutlineSeparated() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full space-y-2" defaultValue={["item-1"]}>
{faqItemsFull.map((item) => (
<AccordionItem key={item.value} value={item.value} className="rounded-md border!">
<AccordionTrigger className="px-5">{item.title}</AccordionTrigger>
<AccordionContent className="text-muted-foreground px-5">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Box
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionBox() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full rounded-md border" defaultValue={["item-1"]}>
{faqItemsFull.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger className="px-5">{item.title}</AccordionTrigger>
<AccordionContent className="text-muted-foreground px-5">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Tabs
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionTabs() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{faqItemsFull.map((item) => (
<AccordionItem
key={item.value}
value={item.value}
className="data-open:bg-accent rounded-md border-none px-5 transition-colors duration-200"
>
<AccordionTrigger>{item.title}</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Tabs Outline
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { PlusIcon } from "lucide-react"
export function AccordionTabsOutline() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{faqItemsFull.map((item) => (
<AccordionItem
key={item.value}
value={item.value}
className="data-open:border-border rounded-md border border-transparent px-5 transition-colors duration-200 data-open:border data-open:shadow-md"
>
<div className="flex">
<AccordionTrigger
data-slot="accordion-trigger"
className="focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-center justify-between gap-4 rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
>
{item.title}
<PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
</AccordionTrigger>
</div>
<AccordionContent className="text-muted-foreground">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}With Media
You'll receive tracking information via email once your order ships.

tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionWithMedia() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full" defaultValue={["item-1"]}>
{mediaItems.map((item) => (
<AccordionItem key={item.value} value={item.value}>
<AccordionTrigger>
<span className="flex items-center gap-4">
<item.icon className="size-4 shrink-0" />
<span>{item.title}</span>
</span>
</AccordionTrigger>
<AccordionContent className="space-y-4">
<p className="text-muted-foreground">{item.content}</p>
<img src={item.media} alt={item.title} className="w-full rounded-md" />
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Filled Header
You can track your order by logging into your account and visiting the "Orders" section. You'll receive tracking information via email once your order ships. For real-time updates, you can also use the tracking number provided in your shipping confirmation email.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
export function AccordionFilledHeader() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full space-y-2" defaultValue={["item-1"]}>
{faqItemsFull.map((item) => (
<AccordionItem key={item.value} value={item.value} className="rounded-md border!">
<AccordionTrigger className="bg-accent px-5 data-open:rounded-b-none">{item.title}</AccordionTrigger>
<AccordionContent className="text-muted-foreground px-5 pt-4">{item.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Multilevel
You can track your order by logging into your account and visiting the "Orders" section.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@polyui/react/collapsible"
import { PlusIcon, ChevronDownIcon } from "lucide-react"
export function AccordionMultilevel() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full rounded-md border" defaultValue={["item-1"]}>
{multilevelItems.map((item) => (
<AccordionItem
key={item.value}
value={item.value}
className="outline-none first:rounded-t-md last:rounded-b-md"
>
<div className="flex">
<AccordionTrigger
data-slot="accordion-trigger"
className="focus-visible:border-ring focus-visible:ring-ring/50 flex w-full flex-1 items-start justify-between gap-4 rounded-lg px-5 py-4 text-left text-sm font-medium transition-all outline-none hover:underline disabled:pointer-events-none disabled:opacity-50 aria-expanded:[&>svg.plus-icon]:rotate-45"
>
<span className="flex items-center gap-4">
<item.icon className="size-4 shrink-0" />
<span>{item.category}</span>
</span>
<PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
</AccordionTrigger>
</div>
<AccordionContent className="pb-0">
{item.faqs.map((faq, i) => (
<Collapsible key={i} className="bg-accent border-t px-5" defaultOpen={i === 0}>
<CollapsibleTrigger className="focus-visible:ring-ring/50 flex w-full items-center gap-4 rounded-sm py-4 font-medium outline-none focus-visible:z-10 focus-visible:ring-[3px]">
<ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0" />
{faq.title}
</CollapsibleTrigger>
<CollapsibleContent className="text-muted-foreground overflow-hidden pb-4">
{faq.content}
</CollapsibleContent>
</Collapsible>
))}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Multilevel Plus
You can track your order by logging into your account and visiting the "Orders" section.
tsx
import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@polyui/react/accordion"
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@polyui/react/collapsible"
import { PlusIcon } from "lucide-react"
export function AccordionMultilevelPlus() {
return (
<div className="flex flex-col gap-8 w-full max-w-lg">
<Accordion className="w-full rounded-md border" defaultValue={["item-1"]}>
{multilevelItems.map((item) => (
<AccordionItem
key={item.value}
value={item.value}
className="outline-none first:rounded-t-md last:rounded-b-md"
>
<AccordionTrigger className="px-5 outline-none focus-visible:ring-0">{item.category}</AccordionTrigger>
<AccordionContent className="pb-0">
{item.faqs.map((faq, i) => (
<Collapsible key={i} className="bg-muted border-t px-8" defaultOpen={i === 0}>
<CollapsibleTrigger className="focus-visible:ring-ring/50 flex w-full items-center gap-4 rounded-sm py-4 font-medium outline-none focus-visible:z-10 focus-visible:ring-[3px] aria-expanded:[&>svg.plus-icon]:rotate-45">
<PlusIcon className="plus-icon text-muted-foreground pointer-events-none size-4 shrink-0 transition-transform duration-200" />
{faq.title}
</CollapsibleTrigger>
<CollapsibleContent className="text-muted-foreground overflow-hidden pb-4">
{faq.content}
</CollapsibleContent>
</Collapsible>
))}
</AccordionContent>
</AccordionItem>
))}
</Accordion>
</div>
)
}Props — Accordion
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
openMultiple | boolean | false | Whether multiple panels can be open simultaneously |
defaultValue | string | string[] | — | Default open panel value(s) in uncontrolled mode |
value | string | string[] | — | Controlled open panel value(s) |
onValueChange | (value: string | string[]) => void | — | Callback when open state changes |
className | string | — | Additional CSS class names |
Props — AccordionItem
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | — | Unique identifier for this panel (required) |
disabled | boolean | false | Whether this panel is disabled |
className | string | — | Additional CSS class names |