Skip to content
Drivn
5 min read

How to Add an Accordion to a React App

Step-by-step guide to adding a copy-and-own Accordion to any React project using the Drivn CLI — zero runtime UI deps, dot notation, and Tailwind.

An accordion looks like a weekend component right up until it is not. The markup is easy; the details are not. You have to track which panels are open, animate height without the jump a naive max-height transition produces, rotate the chevron in sync, and make sure a screen reader announces the collapsed state. Build it by hand and you rediscover every one of those edge cases in order. Pull in a headless primitive like Radix and you trade the edge cases for a runtime dependency, a context provider, and a data-state styling contract you now have to learn.

Drivn takes neither path. The CLI writes an Accordion source file straight into your project — real TypeScript you can open, read, and edit — with no third-party UI package sitting behind it. The open state, the grid-rows height animation, and the ARIA wiring are all right there in a single file you own outright. This guide adds that Accordion to an existing React app in about ten minutes: install the CLI, copy the component in, render it on a page, and retune the style tokens to match your theme.

The steps are identical whether you run Vite, Next.js App Router, or Remix — any React setup with Tailwind and TypeScript works. For setup context see the installation page; for the App Router variant see the Next.js Accordion guide; for live patterns see the Accordion examples page.

Prerequisites

The Accordion assumes three things about your React project, and all three come free from the common scaffolds. First, Tailwind CSS v4 installed and processing your styles — the default look lives entirely in Tailwind class strings, so without a Tailwind build step the component renders unstyled. Second, TypeScript, since the component ships as a .tsx source file. Third, a @/ path alias pointing at your source directory so the import line resolves. Anything created with create-next-app, npm create vite, or npx drivn@latest create already has all three wired up; for a hand-rolled setup, check the compilerOptions.paths entry in your tsconfig.json against the minimal config on the installation page. The only runtime dependency is lucide-react, used for the chevron icon, and the CLI adds it automatically when it is missing from your package.json. No PostCSS plugins, Babel presets, or Tailwind config changes are needed.

Step 1 — Install Drivn via the CLI

Run the CLI from your project root to add the accordion source file. The command prompts once for your install directory (defaulting to src/components/ui/), then writes accordion.tsx and adds lucide-react to your package.json if it is not already present. No global config file is created — the Accordion is just a TypeScript file in your repo that you can edit like any other component. Confirm the file landed correctly with ls or in your editor, then commit the change. If your project uses a monorepo layout or a non-standard path, the CLI docs cover the --cwd and --dir flags used to target a custom location during install.

1# add the Accordion to your existing React project
2npx drivn add accordion
3
4# verify the file was written
5ls src/components/ui/accordion.tsx

Step 2 — Import and render the Accordion

Open the page where the Accordion should live and import the compound component from your UI directory. The API uses dot notation — one import exposes Accordion, Accordion.Item, Accordion.Trigger, and Accordion.Content — so every accordion on every page uses a single import line. Each Accordion.Item needs a unique value string; the root tracks which items are open in a local Set keyed by those values. Render the same accordion on a landing page, inside a dialog, or as a filter panel — the component does not care about its container. See the Accordion docs for the full prop table and every variant, and the examples page for five complete patterns you can copy.

1import { Accordion } from '@/components/ui/accordion'
2
3export function Faq() {
4 return (
5 <Accordion>
6 <Accordion.Item value="what">
7 <Accordion.Trigger>What is Drivn?</Accordion.Trigger>
8 <Accordion.Content>
9 A component library that writes source files into your repo.
10 </Accordion.Content>
11 </Accordion.Item>
12 <Accordion.Item value="price">
13 <Accordion.Trigger>Is it free?</Accordion.Trigger>
14 <Accordion.Content>
15 Yes. MIT-licensed. Copy and ship.
16 </Accordion.Content>
17 </Accordion.Item>
18 </Accordion>
19 )
20}

Step 3 — Customize variants and chevron icon

Because the Accordion lives in your codebase, customization is a source edit rather than a prop API. Open src/components/ui/accordion.tsx and look for the const styles object near the top — it holds every class name the component uses. Change styles.trigger for the trigger hover state, styles.panel for the animation timing, or swap the ChevronDown import for any other Lucide icon to change the visual indicator. The theming page lists every color token the Accordion reads from, so you can override them globally in src/styles/globals.css to re-theme every accordion instance at once. To allow multiple items open simultaneously, pass the multiple prop on the root; pair it with defaultValue={["a", "b"]} to pre-open a subset on mount.

1// src/components/ui/accordion.tsx — top of file
2import { ChevronDown } from 'lucide-react' // swap for Plus, Minus, etc.
3
4const styles = {
5 base: 'w-full divide-y divide-border border-y border-border',
6 trigger: cn(
7 'flex items-center justify-between w-full py-4',
8 'text-sm font-medium text-foreground',
9 'hover:text-foreground/80 transition-colors cursor-pointer'
10 ),
11 icon: cn(
12 'w-4 h-4 text-muted-foreground',
13 'transition-transform duration-200'
14 ),
15 panel: 'grid transition-[grid-template-rows] duration-200',
16 content: 'overflow-hidden text-sm text-muted-foreground',
17}

Step 4 — Wire keyboard and ARIA expectations

Drivn's Accordion triggers are native <button> elements, which gives you keyboard Tab focus, Enter and Space to toggle, and form-safe click handling for free. The component emits aria-expanded on each trigger and wraps content in a role="region" container, so screen readers announce collapsed and expanded states correctly. For long lists of accordion items where Arrow-key navigation between triggers is expected — an accessibility audit often flags this for help centers with thirty or more panels — Drivn does not implement a roving tabindex out of the box. Users Tab through each trigger in document order instead. If Arrow-key cycling is a hard requirement for your audience, the shadcn/ui comparison walks through what Radix offers at the cost of bundling the Radix runtime.

Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

Follow Drivn updates
New components, improvements, and guides every release.
Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

Yes. Set your CRA project up with TypeScript and Tailwind v4 first, then run the Drivn CLI. Tailwind v4 has its own short install guide, and making sure a tsconfig.json is present is enough for the CLI to drop the .tsx component into place. The accordion file has no dependencies on React Router, Next.js routing, or any framework-specific API — it renders anywhere React and Tailwind render to the DOM.

Not out of the box. The component's default styles live in Tailwind class strings — without a Tailwind build step those classes do nothing at runtime. If your project uses another CSS system, replace the class strings in the styles object with your own class names or inline styles. The animation and state logic will continue to work; only the visual layer needs a port from Tailwind utilities to your preferred CSS tool.

Copy-and-own components avoid two problems with traditional npm packages: breaking upgrades that change internal APIs, and the lock-in of not being able to edit internals. With Drivn you can adjust the animation timing, change a default variant, or fix a bug in five seconds without opening a PR upstream. The trade-off is that upgrades are manual — you choose when and whether to pull new CLI versions and re-run the install command.

Yes. The component has no position constraints and can render inside any container that supports arbitrary children. A common pattern is an FAQ accordion inside a feedback dialog, or an options list inside a mobile drawer. The internal focus and keyboard handling adapt to the parent automatically — Tab navigation flows naturally from the surrounding elements into the accordion triggers and back out without extra configuration.