FAQ accordion
A single list of questions with native details and summary disclosure.
npx shadcn@latest add https://ui.goodtabs.co/r/faq-accordion.json
With the @goodtabs namespace in components.json: npx shadcn@latest add @goodtabs/faq-accordion
Is it free to use?
Yes. Every component is MIT licensed and free to copy into a commercial product.
How do I install one?
Run the shadcn add command shown on the component page. The source lands in your repo.
Does it work with my agent?
Each page has a prompt your agent can read, with props, usage and gotchas.
Three questions, the first open.
One at a time
Can I get a refund?
Within 14 days of purchase, no questions asked.
What happens when I cancel?
Your plan runs to the end of the period, then drops to Free.
Can I move seats between people?
Yes. Reassign a seat from settings at any time.
A shared name closes the others when one opens.
Usage
import { FaqAccordion } from "@/components/faq-accordion"
<FaqAccordion name="faq" items={[{ question: "Is it free?", answer: "Yes, MIT licensed." }]} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items | FaqItem[] | question and answer, in order. | |
| name | string | Shared name so opening one closes the rest. | |
| defaultOpen | number | Index of an item that starts open. |
Source
faq-accordion/faq-accordion.tsx
import * as React from "react";
import { ChevronDownIcon } from "lucide-react";
import { cn } from "@/lib/utils";
type FaqItem = {
/** The question. Plain text, it becomes the summary. */
question: string;
/** The answer. A string or your own element. */
answer: React.ReactNode;
};
type FaqAccordionProps = React.ComponentProps<"div"> & {
/** Questions in order. */
items: FaqItem[];
/** Give every item the same name so opening one closes the others. */
name?: string;
/** Index of an item that starts open. */
defaultOpen?: number;
};
/**
* A single list of questions. Each item is a native details and summary pair, so the disclosure
* semantics, keyboard and find in page come from the browser. No JavaScript, server component.
*/
export function FaqAccordion({ items, name, defaultOpen, className, ...props }: FaqAccordionProps) {
return (
<div data-slot="faq-accordion" className={cn("w-full divide-y rounded-lg border bg-card", className)} {...props}>
{items.map((item, i) => (
<details key={item.question} name={name} open={i === defaultOpen} className="group/faq">
<summary className="flex min-h-11 cursor-pointer list-none items-center justify-between gap-4 px-4 py-3 text-sm font-medium text-foreground outline-none select-none hover:bg-accent/40 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:ring-inset [&::-webkit-details-marker]:hidden">
{item.question}
<ChevronDownIcon
className="size-4 shrink-0 text-muted-foreground transition-transform duration-150 group-open/faq:rotate-180 motion-reduce:transition-none"
aria-hidden="true"
/>
</summary>
<div className="px-4 pb-4 text-sm text-muted-foreground">{item.answer}</div>
</details>
))}
</div>
);
}
export type { FaqItem };
For AI agents
Hand your agent this page, the markdown at /components/faq-accordion/prompt.md, or the Copy AI prompt button above.
When to use
- A short FAQ of three to eight questions
- When find in page and no JavaScript matter
- Pricing, signup and support pages
When not to use
- For grouped questions, use faq-tabs
- For app navigation with nested items
- When panels need animated height
Agent notes
- Server component built on details and summary, so semantics and keyboard are native
- The name attribute gives exclusive opening in current browsers
- Chevron rotates 150ms and is removed under reduced motion
- Items are keyed by question text, keep questions unique