Accordion
Organizes content into collapsible sections, allowing users to focus on one section at a time.
<script lang="ts">
import { Accordion } from "bits-ui";
import { CaretDown } from "$icons/index.js";
const items = [
{
title: "What is the meaning of life?",
content:
"To become a better person, to help others, and to leave the world a better place than you found it."
},
{
title: "How do I become a better person?",
content:
"Read books, listen to podcasts, and surround yourself with people who inspire you."
},
{
title: "What is the best way to help others?",
content: "Give them your time, attention, and love."
}
];
let value = $state<string[]>([]);
</script>
<Accordion.Root class="w-full sm:max-w-[70%]" type="multiple" bind:value>
{#each items as item, i}
<Accordion.Item value={`${i}`} class="group border-b border-dark-10 px-1.5">
<Accordion.Header>
<Accordion.Trigger
class="flex w-full flex-1 items-center justify-between py-5 text-[15px] font-medium transition-all [&[data-state=open]>span>svg]:rotate-180"
>
{item.title}
<span
class="inline-flex size-8 items-center justify-center rounded-[7px] bg-transparent transition-all hover:bg-dark-10"
>
<CaretDown class="size-[18px] transition-all duration-200" />
</span>
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content
class="overflow-hidden text-sm tracking-[-0.01em] data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
>
<div class="pb-[25px]">
{item.content}
</div>
</Accordion.Content>
</Accordion.Item>
{/each}
</Accordion.Root>
Structure
<script lang="ts">
import { Accordion } from "bits-ui";
</script>
<Accordion.Root>
<Accordion.Item>
<Accordion.Header>
<Accordion.Trigger />
</Accordion.Header>
<Accordion.Content />
</Accordion.Item>
</Accordion.Root>
Usage
Single
Set the type
prop to "single"
to allow only one accordion item to be open at a time.
<Accordion.Root type="single">
<!-- ... -->
</Accordion.Root>
Multiple
Set the type
prop to "multiple"
to allow multiple accordion items to be open at the same time.
<Accordion.Root type="multiple">
<!-- ... -->
</Accordion.Root>
Disable Items
To disable an individual accordion item, set the disabled
prop to true
. This will prevent users from interacting with the item.
<Accordion.Root type="single">
<Accordion.Item value="item-1" disabled>
<!-- ... -->
</Accordion.Item>
</Accordion.Root>
Controlled Value
You can programmatically control the active of the accordion item(s) using the value
prop.
<script lang="ts">
let value = $state("item-1");
</script>
<button onclick={() => (value = "item-2")}>Change value</button>
<Accordion.Root bind:value>
<!-- ... -->
</Accordion.Root>
Value Change Side Effects
You can use the onValueChange
prop to handle side effects when the value of the accordion changes.
<Accordion.Root
onValueChange={(value) => {
doSomething(value);
}}
>
<!-- ... -->
</Accordion.Root>
Alternatively, you can use bind:value
with an $effect
block to handle side effects when the value of the accordion changes.
<script lang="ts">
import { Accordion } from "bits-ui";
let value = $state("item-1")
$effect(() => {
doSomething(value);
})
</script>
<Accordion.Root bind:value>
<!-- ... -->
</Accordion.Item>
Reusable Wrappers
Entire Component
If you're going to be using the same accordion component multiple places throughout your app, you can create a reusable wrapper to reduce the amount of code you need to write each time.
<script lang="ts">
import { Accordion, type WithoutChildren } from "bits-ui";
type Props = WithoutChildren<Accordion.RootProps> & {
items: Array<{
value: string;
disabled?: boolean;
title: string;
content: string;
}>;
};
let { items, value = $bindable(""), ...restProps }: Props = $props();
</script>
<Accordion.Root bind:value {...restProps}>
{#each items as item}
<Accordion.Item value={item.value} disabled={item.disabled}>
<Accordion.Header>
<Accordion.Trigger>{item.title}</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>{item.content}</Accordion.Content>
</Accordion.Item>
{/each}
</Accordion.Root>
Individual Item
For each invidual item, you need an Accordion.Item
, Accordion.Header
, Accordion.Trigger
and Accordion.Content
component. You can make a reusable wrapper to reduce the amount of code you need to write each time.
<script lang="ts">
import { Accordion, type WithoutChildren } from "bits-ui";
type Props = WithoutChildren<Accordion.ItemProps> & {
title: string;
content: string;
};
let { title, content, ...restProps }: Props = $props();
</script>
<Accordion.Item {...restProps}>
<Accordion.Header>
<Accordion.Trigger>
{title}
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
{content}
</Accordion.Content>
</Accordion.Item>
<script lang="ts">
import { Accordion } from "bits-ui";
import CustomAccordionItem from "$lib/components/CustomAccordionItem.svelte";
</script>
<Accordion.Root type="single">
<CustomAccordionItem title="Item 1" content="Content 1" />
<CustomAccordionItem title="Item 2" content="Content 2" />
<CustomAccordionItem title="Item 3" content="Content 3" />
</Accordion.Root>
API Reference
The root accordion component used to set and manage the state of the accordion.
Property | Type | Description |
---|---|---|
multiple | boolean | Whether or not multiple accordion items can be active at the same time. Default: false |
disabled | boolean | Whether or not the accordion is disabled. Default: false |
value | union | The active accordion item value. Default: undefined |
onValueChange | function | A callback function called when the active accordion item value changes. Default: undefined |
asChild | boolean | Whether to use render delegation with this component or not. Default: false |
el | HTMLDivElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default: undefined |
Slot Property | Type | Description |
---|---|---|
builder | object | The builder attributes and actions to apply to the element if using the |
Data Attribute | Value | Description |
---|---|---|
data-orientation | enum | The orientation of the accordion. |
data-accordion-root |
| Present on the root element. |
An accordion item.
Property | Type | Description |
---|---|---|
value * Required | string | The value of the accordion item. Default: undefined |
disabled | boolean | Whether or not the accordion item is disabled. Default: false |
asChild | boolean | Whether to use render delegation with this component or not. Default: false |
el | HTMLDivElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default: undefined |
Slot Property | Type | Description |
---|---|---|
builder | object | The builder attributes and actions to apply to the element if using the |
Data Attribute | Value | Description |
---|---|---|
data-state | enum | The state of the accordion item. |
data-disabled |
| Present when the accordion item is disabled. |
data-accordion-item | —— | Present on the item element. |
The accordion item header, which wraps the trigger and makes it more accessible.
Property | Type | Description |
---|---|---|
level | enum | The heading level to use for the header. This will be set as the Default: undefined |
asChild | boolean | Whether to use render delegation with this component or not. Default: false |
el | HTMLDivElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default: undefined |
Slot Property | Type | Description |
---|---|---|
builder | object | The builder attributes and actions to apply to the element if using the |
Data Attribute | Value | Description |
---|---|---|
data-heading-level | enum | The heading level of the header. |
data-accordion-header | —— | Present on the header element. |
The accordion item trigger, which opens and closes the accordion item.
Property | Type | Description |
---|---|---|
asChild | boolean | Whether to use render delegation with this component or not. Default: false |
el | HTMLButtonElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default: undefined |
Slot Property | Type | Description |
---|---|---|
builder | object | The builder attributes and actions to apply to the element if using the |
Data Attribute | Value | Description |
---|---|---|
data-state | enum | The state of the accordion item. |
data-disabled | —— | Present when the accordion item is disabled. |
data-value | —— | The value of the accordion item. |
data-accordion-trigger | —— | Present on the trigger element. |
The accordion item content, which is displayed when the item is open.
Property | Type | Description |
---|---|---|
transition | function | A Svelte transition function to use when transitioning the content in and out. Default: undefined |
transitionConfig | TransitionConfig | The configuration to apply to the transition. Default: undefined |
inTransition | function | A Svelte transition function to use when transitioning the content in and out. Default: undefined |
inTransitionConfig | TransitionConfig | The configuration to apply to the transition. Default: undefined |
outTransition | function | A Svelte transition function to use when transitioning the content in and out. Default: undefined |
outTransitionConfig | TransitionConfig | The configuration to apply to the transition. Default: undefined |
asChild | boolean | Whether to use render delegation with this component or not. Default: false |
el | HTMLDivElement | The underlying DOM element being rendered. You can bind to this to programatically interact with the element. Default: undefined |
Slot Property | Type | Description |
---|---|---|
builder | object | The builder attributes and actions to apply to the element if using the |
Data Attribute | Value | Description |
---|---|---|
data-state | enum | The state of the accordion item. |
data-disabled | —— | Present when the accordion item is disabled. |
data-value | —— | The value of the accordion item. |
data-accordion-content | —— | Present on the content element. |