Accordion
Accordion groups a collection of contents in panels.
Usage#
import { Accordion } from '@primereact/ui/accordion';<Accordion.Root>
<Accordion.Panel value="1">
<Accordion.Header>
<Accordion.Trigger>Title</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>Content</Accordion.Content>
</Accordion.Panel>
</Accordion.Root>Examples#
Multiple#
Each Accordion.Panel must contain a unique value property to specify the active item. Only one panel at a time can be active by default, enabling multiple property on Accordion.Root changes this behavior to allow multiple panels to be open simultaneously.
import { ChevronDown } from '@primeicons/react/chevron-down';
import { Accordion } from '@primereact/ui/accordion';
export default function MultipleDemo() {
return (
<div>
<Accordion.Root multiple className="max-w-md mx-auto">
<Accordion.Panel value="1">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
What is this service about?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
This service helps you manage your projects more efficiently by offering real-time collaboration, task tracking, and
powerful analytics. Whether you`re working solo or in a team, it`s built to scale with your needs.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="2">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
Is my data secure?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Yes. We use end-to-end encryption and follow industry best practices to ensure your data is protected. Your information is
stored on secure servers and regularly backed up.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="3">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
Can I upgrade or downgrade my plan later?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Absolutely. You can change your subscription plan at any time from your account settings. Changes take effect immediately,
and any billing adjustments are handled automatically.
</p>
</Accordion.Content>
</Accordion.Panel>
</Accordion.Root>
</div>
);
}
Controlled#
Accordion can be used in controlled mode by using value and onValueChange props. This allows you to manage the active panel state externally.
This service helps you manage your projects more efficiently by offering real-time collaboration, task tracking, and powerful analytics. Whether you're working solo or in a team, it's built to scale with your needs.
'use client';
import { ChevronDown } from '@primeicons/react/chevron-down';
import type { AccordionRootValueChangeEvent } from '@primereact/types/shared/accordion';
import { Accordion } from '@primereact/ui/accordion';
import { Button } from '@primereact/ui/button';
import { useState } from 'react';
export default function ControlledDemo() {
const [activePanel, setActivePanel] = useState<string | null>('1');
return (
<div className="space-y-4">
<div className="flex gap-2 justify-center">
<Button onClick={() => setActivePanel('1')} severity={activePanel === '1' ? 'primary' : 'secondary'}>
Panel 1
</Button>
<Button onClick={() => setActivePanel('2')} severity={activePanel === '2' ? 'primary' : 'secondary'}>
Panel 2
</Button>
<Button onClick={() => setActivePanel('3')} severity={activePanel === '3' ? 'primary' : 'secondary'}>
Panel 3
</Button>
<Button onClick={() => setActivePanel(null)} severity="danger">
Close All
</Button>
</div>
<Accordion.Root
className="max-w-md mx-auto"
value={activePanel}
onValueChange={(e: AccordionRootValueChangeEvent) => setActivePanel(e.value as string | null)}
>
<Accordion.Panel value="1">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
What is this service about?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
This service helps you manage your projects more efficiently by offering real-time collaboration, task tracking, and
powerful analytics. Whether you're working solo or in a team, it's built to scale with your needs.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="2">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
Is my data secure?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Yes. We use end-to-end encryption and follow industry best practices to ensure your data is protected. Your information is
stored on secure servers and regularly backed up.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="3">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
Can I upgrade or downgrade my plan later?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Absolutely. You can change your subscription plan at any time from your account settings. Changes take effect immediately,
and any billing adjustments are handled automatically.
</p>
</Accordion.Content>
</Accordion.Panel>
</Accordion.Root>
</div>
);
}
Trigger#
The Accordion.Trigger component is used to toggle the visibility of the content. It can be customized by passing a function that receives the accordionpanel instance or using data-content-open attribute for styling.
Is my data secure?
'use client';
import { Minus } from '@primeicons/react/minus';
import { Plus } from '@primeicons/react/plus';
import type { AccordionTriggerInstance } from '@primereact/types/shared/accordion';
import { Accordion } from '@primereact/ui/accordion';
export default function TriggerDemo() {
return (
<div>
<Accordion.Root className="max-w-md mx-auto" multiple>
<Accordion.Panel value="1">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full group">
What is this service about?
<Plus className="group-[[data-content-open]>&]:rotate-45 transition-transform ease-out" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
This service helps you manage your projects more efficiently by offering real-time collaboration, task tracking, and
powerful analytics. Whether you're working solo or in a team, it's built to scale with your needs.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="2">
<Accordion.Header>
<p className="pl-4">Is my data secure?</p>
<Accordion.Trigger className="flex justify-between items-center">
{({ accordionpanel }: AccordionTriggerInstance) => (accordionpanel?.active ? <Minus /> : <Plus />)}
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Yes. We use end-to-end encryption and follow industry best practices to ensure your data is protected. Your information is
stored on secure servers and regularly backed up.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="3">
<Accordion.Header>
<Accordion.Trigger className="flex justify-start items-center w-full gap-2">
<Plus className="[[data-content-open]>&]:rotate-45 transition-transform ease-out" />
Can I upgrade or downgrade my plan later?
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Absolutely. You can change your subscription plan at any time from your account settings. Changes take effect immediately,
and any billing adjustments are handled automatically.
</p>
</Accordion.Content>
</Accordion.Panel>
</Accordion.Root>
</div>
);
}
Disabled#
Enabling disabled property of an Accordion.Panel prevents user interaction of that panel. Alternatively, enabling disabled on Accordion.Root disables all panels.
import { ChevronDown } from '@primeicons/react/chevron-down';
import { Accordion } from '@primereact/ui/accordion';
export default function DisabledDemo() {
return (
<div className="space-y-8">
<Accordion.Root disabled className="max-w-md mx-auto">
<Accordion.Panel value="1">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
How do I reset my password?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
You can reset your password by clicking the "Forgot password?" link on the login page. We'll send a password reset link to
your registered email address.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="2">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
Do you offer team accounts?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Yes. Our Team and Business plans are designed for collaboration. You can invite team members, assign roles, and manage
permissions easily from your dashboard.
</p>
</Accordion.Content>
</Accordion.Panel>
</Accordion.Root>
<Accordion.Root className="max-w-md mx-auto">
<Accordion.Panel value="1">
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
What happens if I exceed my usage limit?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
If you go over your plan limits (e.g., storage or API requests), you'll receive a notification. You can either upgrade
your plan or wait until the next billing cycle resets.
</p>
</Accordion.Content>
</Accordion.Panel>
<Accordion.Panel value="2" disabled>
<Accordion.Header>
<Accordion.Trigger className="flex justify-between items-center w-full">
Is there a mobile app available?
<ChevronDown className="transition-transform duration-200 [[data-content-open]>&]:rotate-180" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<p className="text-sm">
Yes, we offer both iOS and Android apps so you can manage your account and stay connected on the go.
</p>
</Accordion.Content>
</Accordion.Panel>
</Accordion.Root>
</div>
);
}
Template#
Accordion panels can be created dynamically by iterating over a data source. This approach allows for cleaner code when rendering multiple panels with consistent structure and custom styling.
'use client';
import { CreditCard } from '@primeicons/react/credit-card';
import { Lock } from '@primeicons/react/lock';
import { Plus } from '@primeicons/react/plus';
import { QuestionCircle } from '@primeicons/react/question-circle';
import { Accordion } from '@primereact/ui/accordion';
const items = [
{
label: 'What is this service about?',
value: '1',
icon: QuestionCircle,
color: 'text-yellow-500',
content:
"This service helps you manage your projects more efficiently by offering real-time collaboration, task tracking, and powerful analytics. Whether you're working solo or in a team, it's built to scale with your needs."
},
{
label: 'Is my data secure?',
value: '2',
icon: Lock,
color: 'text-blue-500',
content:
'Yes. We use end-to-end encryption and follow industry best practices to ensure your data is protected. Your information is stored on secure servers and regularly backed up.'
},
{
label: 'Can I upgrade or downgrade my plan later?',
value: '3',
icon: CreditCard,
color: 'text-green-500',
content:
'Absolutely. You can change your subscription plan at any time from your account settings. Changes take effect immediately, and any billing adjustments are handled automatically.'
}
];
export default function TemplateDemo() {
return (
<div>
<Accordion.Root className="max-w-md mx-auto border border-surface-200 dark:border-surface-700 rounded-md divide-y divide-surface-200 dark:divide-surface-700">
{items.map((item) => (
<Accordion.Panel key={item.value} value={item.value} className="last:border-none transition-all ease-out">
<Accordion.Header className="bg-transparent">
<Accordion.Trigger className="flex justify-between items-center w-full">
<span className="flex items-center gap-4">
{item.icon && <item.icon className={item.color} />}
<span className="font-medium">{item.label}</span>
</span>
<Plus className="transition-transform ease-out [[data-content-open]>&]:rotate-45" />
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content
className="bg-transparent px-4 leading-6 pl-8"
pt-pccollapsible-content={{ inner: { className: 'bg-transparent' } }}
>
<p className="text-sm">{item.content}</p>
</Accordion.Content>
</Accordion.Panel>
))}
</Accordion.Root>
</div>
);
}
With RadioButton#
RadioButton component can be used to group multiple Accordion.Panel components.
Perfect for individuals getting started. Includes access to core components and community support.
'use client';
import type { useAccordionChangeEvent } from '@primereact/types/shared/accordion';
import type { RadioButtonGroupValueChangeEvent } from '@primereact/types/shared/radiobutton';
import { Accordion } from '@primereact/ui/accordion';
import { Button } from '@primereact/ui/button';
import { RadioButton } from '@primereact/ui/radiobutton';
import { RadioButtonGroup } from '@primereact/ui/radiobuttongroup';
import * as React from 'react';
const items = [
{
label: 'Starter Plan',
description: 'Perfect for individuals getting started. Includes access to core components and community support.',
value: '1',
price: '$99'
},
{
label: 'Growth Plan',
description: 'Ideal for freelancers and small teams. Unlocks advanced UI components and priority email support.',
value: '2',
price: '$249'
},
{
label: 'Scale Plan',
description: 'Best for growing businesses. Includes all features, early access to new releases, and Slack support.',
value: '3',
price: '$499'
}
];
export default function WithRadioButtonDemo() {
const [selected, setSelected] = React.useState<string>('1');
return (
<div>
<div className="max-w-md mx-auto w-full">
<RadioButtonGroup
className="w-full"
value={selected}
onValueChange={(e: RadioButtonGroupValueChangeEvent) => setSelected(e.value as string)}
>
<Accordion.Root
value={selected}
onChange={(e: useAccordionChangeEvent) => setSelected(e.value as string)}
className="w-full border border-surface-200 dark:border-surface-700 rounded-md divide-y divide-surface-200 dark:divide-surface-700"
>
{items.map((item) => (
<Accordion.Panel key={item.value} value={item.value} className="last:border-none transition-all ease-out">
<Accordion.Header
onClick={() => setSelected(item.value)}
className="flex items-center justify-between bg-transparent py-3"
>
<span className="flex items-center gap-4">
<RadioButton.Root inputId={`radio-${item.value}`} name="price" value={item.value} />
<span className="font-semibold text-base">{item.label}</span>
</span>
<span className="font-semibold text-base">{item.price}</span>
</Accordion.Header>
<Accordion.Content className="bg-transparent px-4 pb-3.5 leading-6 pl-12.5">
<p className="text-sm">{item.description}</p>
</Accordion.Content>
</Accordion.Panel>
))}
</Accordion.Root>
</RadioButtonGroup>
<Button className="w-full mt-4" size="large">
Buy Now for {items.find((item) => item.value === selected)?.price}
</Button>
</div>
</div>
);
}
Accessibility#
Screen Reader#
Accordion header elements is a button element and use aria-controls to define the id of the content section along with aria-expanded for the visibility state. The value to read a header element defaults to the value of the header property and can be customized by defining an aria-label or aria-labelledby via the pt property.
The content uses region role, defines an id that matches the aria-controls of the header and aria-labelledby referring to the id of the header.
Header Keyboard Support#
| Key | Function |
|---|---|
tab | Moves focus to the next focusable element in the page tab sequence. |
shift + tab | Moves focus to the previous focusable element in the page tab sequence. |
enter | Toggles the visibility of the content. |
space | Toggles the visibility of the content. |
down arrow | Moves focus to the next header. If focus is on the last header, moves focus to the first header. |
up arrow | Moves focus to the previous header. If focus is on the first header, moves focus to the last header. |
home | Moves focus to the first header. |
end | Moves focus to the last header. |