Accordion
Displays collapsible content panels that reveal or hide sections of content.
Docsimport {
Accordion,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionBasicDemo() {
return (
<Accordion defaultValue={["item-1"]}>
<AccordionItem value="item-1">
<AccordionItemTrigger>Section Title</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm">Content goes here.</div>
</AccordionItemContent>
</AccordionItem>
</Accordion>
);
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add accordionManual
Create the recipe file at src/components/recipes/accordion.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const accordionVariants = tv({
slots: {
root: "w-full",
item: "border-b border-border",
itemTrigger:
"flex w-full items-center justify-between gap-2 py-4 text-sm font-medium text-start ring-offset-background 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",
itemIndicator:
"size-4 shrink-0 text-foreground transition-transform duration-200 data-[state=open]:rotate-180",
itemContent:
"overflow-hidden transition-all duration-200 data-[state=open]:animate-accordion-expand data-[state=closed]:animate-accordion-collapse",
itemBody: "pb-4 text-sm text-foreground",
},
});
export type AccordionVariants = VariantProps<typeof accordionVariants>;Create the component directory and files.
src/components/accordion/accordion.base.tsx:
import { Accordion as ArkAccordion } from "@ark-ui/solid/accordion";
import { splitProps, type Component } from "solid-js";
import { accordionVariants } from "../recipes/accordion";
const styles = accordionVariants();
const Root: Component<ArkAccordion.RootProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkAccordion.Root class={styles.root({ class: local.class })} {...others} />;
};
const RootProvider: Component<ArkAccordion.RootProviderProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkAccordion.RootProvider class={styles.root({ class: local.class })} {...others} />;
};
const Item: Component<ArkAccordion.ItemProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkAccordion.Item class={styles.item({ class: local.class })} {...others} />;
};
const ItemTrigger: Component<ArkAccordion.ItemTriggerProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return (
<ArkAccordion.ItemTrigger class={styles.itemTrigger({ class: local.class })} {...others} />
);
};
const ItemContent: Component<ArkAccordion.ItemContentProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return (
<ArkAccordion.ItemContent class={styles.itemContent({ class: local.class })} {...others} />
);
};
const ItemIndicator: Component<ArkAccordion.ItemIndicatorProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return (
<ArkAccordion.ItemIndicator class={styles.itemIndicator({ class: local.class })} {...others} />
);
};
export const Accordion = { Root, RootProvider, Item, ItemTrigger, ItemContent, ItemIndicator };src/components/accordion/index.tsx:
import { splitProps, type Component } from "solid-js";
import { Accordion as AccordionBase } from "./accordion.base";
import type { Accordion as ArkAccordion } from "@ark-ui/solid/accordion";
const Accordion = AccordionBase.Root;
const AccordionItem = AccordionBase.Item;
const AccordionItemTrigger: Component<ArkAccordion.ItemTriggerProps> = (props) => {
const [local, others] = splitProps(props, ["class", "children"]);
return (
<AccordionBase.ItemTrigger class={local.class} {...others}>
{local.children}
<AccordionBase.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="m6 9 6 6 6-6" />
</svg>
</AccordionBase.ItemIndicator>
</AccordionBase.ItemTrigger>
);
};
const AccordionItemContent = AccordionBase.ItemContent;
export { Accordion, AccordionItem, AccordionItemTrigger, AccordionItemContent, AccordionBase };
export { accordionVariants, type AccordionVariants } from "../recipes/accordion";Note: Make sure your project has the Tailwind CSS theme variables set up (
--background,--foreground,--ring,--border, etc.) or override the utility classes to match your design system.
Usage
Basic Usage
import {
Accordion,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionBasicDemo() {
return (
<Accordion defaultValue={["item-1"]}>
<AccordionItem value="item-1">
<AccordionItemTrigger>Section Title</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm">Content goes here.</div>
</AccordionItemContent>
</AccordionItem>
</Accordion>
);
}Multiple Items
An accordion with multiple collapsible items.
import {
Accordion,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionMultipleItemsDemo() {
return (
<Accordion defaultValue={["item-1"]}>
<AccordionItem value="item-1">
<AccordionItemTrigger>First Section</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm">First content.</div>
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionItemTrigger>Second Section</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm">Second content.</div>
</AccordionItemContent>
</AccordionItem>
</Accordion>
);
}Multiple
Use the multiple prop to allow more than one item to be expanded at the same time.
import {
Accordion,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionMultipleDemo() {
return (
<div>
<Accordion multiple defaultValue={["item-1", "item-2"]}>
<AccordionItem value="item-1">
<AccordionItemTrigger>Can I open multiple items?</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">
Yes. Just pass the <code>multiple</code> prop to the Accordion.
</div>
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionItemTrigger>How does it work?</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">
Each item tracks its own expanded/collapsed state independently.
</div>
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-3">
<AccordionItemTrigger>Is it accessible?</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">
Yes. It follows the WAI-ARIA Accordion pattern.
</div>
</AccordionItemContent>
</AccordionItem>
</Accordion>
</div>
);
}Controlled
Control the expanded state programmatically using value and onValueChange.
import { createSignal } from "solid-js";
import {
Accordion,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionControlledDemo() {
const [value, setValue] = createSignal<string[]>([]);
return (
<Accordion value={value()} onValueChange={(details) => setValue(details.value)}>
<AccordionItem value="item-1">
<AccordionItemTrigger>Controlled Item</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm">Current state: {value().join(", ") || "closed"}</div>
</AccordionItemContent>
</AccordionItem>
</Accordion>
);
}Disabled
Disable individual items or the entire accordion.
import {
Accordion,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionDisabledDemo() {
return (
<div>
<Accordion>
<AccordionItem value="item-1">
<AccordionItemTrigger>Active Item</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">This item is interactive.</div>
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-2" disabled>
<AccordionItemTrigger>Disabled Item</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">This item is disabled.</div>
</AccordionItemContent>
</AccordionItem>
</Accordion>
</div>
);
}To disable all items at once, pass disabled to the {"<Accordion>"} root.
Advanced Usage
Root Provider
Use AccordionRootProvider when you need to access the accordion state outside of the accordion tree. This pattern uses the useAccordion hook from Ark UI to create a shared context that both the accordion and external elements can reference.
import { useAccordion } from "@ark-ui/solid/accordion";
import {
AccordionBase,
AccordionItem,
AccordionItemTrigger,
AccordionItemContent,
} from "~/components/accordion";
export default function AccordionRootProviderDemo() {
const accordion = useAccordion({ multiple: true, defaultValue: ["item-1"] });
return (
<div class="space-y-4">
<output class="block text-sm text-muted-foreground">
Value: {JSON.stringify(accordion().value)}
</output>
<AccordionBase.RootProvider value={accordion}>
<AccordionItem value="item-1">
<AccordionItemTrigger>What is this demo showing?</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">
The accordion state is managed externally via <code>useAccordion</code>. The{" "}
<code>output</code> element above reads <code>accordion().value</code> outside the
accordion tree.
</div>
</AccordionItemContent>
</AccordionItem>
<AccordionItem value="item-2">
<AccordionItemTrigger>Why use RootProvider?</AccordionItemTrigger>
<AccordionItemContent>
<div class="pb-4 text-sm text-foreground">
It gives you access to the accordion context anywhere — not just inside children of
the root. Useful for external controls, toolbars, or custom UI.
</div>
</AccordionItemContent>
</AccordionItem>
</AccordionBase.RootProvider>
</div>
);
}The key difference:
Accordion(Root) — manages its own state internally. Use for simple, self-contained accordions.AccordionRootProvider— accepts a pre-created accordion context viauseAccordion. Use when you need to read or control the accordion state from outside the component tree.
API Reference
See the Ark UI Accordion documentation.