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 "phosphor-svelte/lib/CaretDown";
 
  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.

CustomAccordion.svelte
	<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>

Since we're populating the children of the Accordion.Root within the component, we've excluded the children snippet prop from the component props using the WithoutChildren type helper.

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.

CustomItem.svelte
	<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>
+page.svelte
	<script lang="ts">
	import { Accordion } from "bits-ui";
	import CustomItem from "$lib/components/CustomItem.svelte";
</script>
 
<Accordion.Root type="single">
	<CustomItem title="Item 1" content="Content 1" />
	<CustomItem title="Item 2" content="Content 2" />
	<CustomItem title="Item 3" content="Content 3" />
</Accordion.Root>

API Reference

Accordion.Root

The root accordion component used to set and manage the state of the accordion.

Property Type Description
type Required
enum

The type of accordion. If set to 'multiple', the accordion will allow multiple items to be open at the same time. If set to single, the accordion will only allow a single item to be open.

Default: undefined
value bindable prop
union

The value of the currently active accordion item. If type is 'single', this should be a string. If type is 'multiple', this should be an array of strings.

Default: undefined
onValueChange
function

A callback function called when the active accordion item value changes. If the type is 'single', the argument will be a string. If type is 'multiple', the argument will be an array of strings.

Default: undefined
disabled
boolean

Whether or not the accordion is disabled. When disabled, the accordion cannot be interacted with.

Default: false
loop
boolean

Whether or not the accordion should loop through items when reaching the end.

Default: false
orientation
enum

The orientation of the accordion.

Default: vertical
child
Snippet

Use render delegation to render your own element. See render delegation for more information.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
ref
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined

Accordion.Item

An accordion item.

Property Type Description
disabled
boolean

Whether or not the accordion item is disabled.

Default: false
value Required
string

The value of the accordion item. This is used to identify when the item is open or closed.

Default: undefined
child
Snippet

Use render delegation to render your own element. See render delegation for more information.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
ref
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined

Accordion.Header

The header of the accordion item.

Property Type Description
level
union

The heading level of the header. This will be set as the aria-level attribute.

Default: 3
child
Snippet

Use render delegation to render your own element. See render delegation for more information.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
ref
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined

Accordion.Trigger

The button responsible for toggling the accordion item.

Property Type Description
disabled
boolean

Whether or not the accordion item trigger is disabled.

Default: false
child
Snippet

Use render delegation to render your own element. See render delegation for more information.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
ref
HTMLButtonElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined

Accordion.Content

The accordion item content, which is displayed when the item is open.

Property Type Description
forceMount
boolean

Whether or not to forcefully mount the content. This is useful if you want to use Svelte transitions or another animnation library for the content.

Default: false
child
Snippet

Use render delegation to render your own element. See render delegation for more information.

Default: undefined
children
Snippet

The children content to render.

Default: undefined
ref
HTMLDivElement

The underlying DOM element being rendered. You can bind to this to get a reference to the element.

Default: undefined