Skip to content
Drivn
5 min read

Drivn vs shadcn/ui — Accordion Component Compared

Side-by-side comparison of Drivn and shadcn/ui React Accordion components — runtime deps, grid animation, keyboard model, API style, and accessibility.

Choosing between Drivn's Accordion and shadcn/ui's Accordion comes down to one question: do you want a component backed by a battle-tested primitive, or one small enough to read in a single sitting? shadcn's Accordion is a Tailwind skin over Radix UI's Accordion primitive, so it inherits WAI-ARIA semantics, a roving tabindex, and animation timing that the Radix team maintains as a runtime dependency. Drivn's Accordion carries no third-party UI code at all — open state lives in a React Set, the chevron is a single lucide-react icon, and the panel expands through a CSS grid-template-rows transition from 0fr to 1fr.

For an everyday FAQ block, users will never feel the difference. For teams that audit each runtime dependency, track bundle weight per component, or simply want to understand every line their accordion runs, the gap is the whole decision. shadcn pulls in Radix's keyboard model plus roughly six kilobytes of primitives; Drivn hands you one file you can scan in half a minute and edit without learning a framework's context API.

The sections below split the comparison into API shape, animation technique, accessibility coverage, customization surface, and the features Drivn deliberately leaves out. Each one shows real code from both sides, so your choice rests on what ships in your bundle rather than on a landing-page promise.

Side-by-side comparison

FeatureDrivnshadcn/ui
Runtime UI dependenciesNone (React + Tailwind)Radix Accordion primitive
Animation techniqueCSS grid rows (0fr → 1fr)Radix data-state + keyframes
Keyboard navigationTab + Enter / SpaceTab + Enter / Space + Arrow keys
Multiple open itemsmultiple proptype="multiple"
Default opendefaultValue (string | string[])defaultValue
API styleDot notation (Accordion.Item)Named exports (AccordionItem)
Estimated bundle size≈1.5 kB≈6 kB (with Radix primitive)
Accessibilityaria-expanded + role=regionFull WAI-ARIA via Radix
LicenseMITMIT
Copy-paste install

API side-by-side

shadcn/ui exports Accordion, AccordionItem, AccordionTrigger, and AccordionContent as four named exports. Drivn exports a single Accordion object with three dot-notation members — Accordion.Item, Accordion.Trigger, and Accordion.Content — so a typical FAQ section needs exactly one import line. The accordion docs walk through the full API.

The mental models diverge further under the hood. shadcn's AccordionItem passes value into Radix context, which synchronizes open state and ARIA attributes across every nested primitive. Drivn's Accordion.Item writes value into a local React context that the trigger and content both read from. The resulting JSX is almost identical — what changes is what you import and what runs at render.

1// shadcn/ui — four named imports
2import {
3 Accordion,
4 AccordionItem,
5 AccordionTrigger,
6 AccordionContent,
7} from '@/components/ui/accordion'
8
9<Accordion type="single" collapsible>
10 <AccordionItem value="item-1">
11 <AccordionTrigger>Is it styled?</AccordionTrigger>
12 <AccordionContent>Yes. Tailwind defaults.</AccordionContent>
13 </AccordionItem>
14</Accordion>
15
16// Drivn — one import, dot notation
17import { Accordion } from '@/components/ui/accordion'
18
19<Accordion>
20 <Accordion.Item value="item-1">
21 <Accordion.Trigger>Is it styled?</Accordion.Trigger>
22 <Accordion.Content>Yes. Tailwind defaults.</Accordion.Content>
23 </Accordion.Item>
24</Accordion>

Animation and motion

The two libraries reach smooth expansion through completely different mechanics. shadcn/ui leans on Radix, which sets data-state="open" or data-state="closed" on each panel and pairs those with accordion-down and accordion-up Tailwind keyframes. Radix measures the panel and injects a CSS variable for its height, which lets the keyframes animate content of unknown size without JavaScript running on every frame.

Drivn takes a leaner route. Its accordion panel is a CSS grid whose grid-template-rows value flips between 0fr when closed and 1fr when open — the exact style={{ gridTemplateRows: isOpen ? '1fr' : '0fr' }} you will find in the installed file. The browser interpolates the fractional row height on its own, so there is no measurement pass, no ResizeObserver, and no injected CSS variable. A single transition-[grid-template-rows] class in the styles.panel string carries the whole animation.

At normal FAQ sizes neither approach drops frames. Drivn wins on how little there is to understand; Radix wins on edge cases — very tall panels, images that finish loading after the open transition, or content that resizes mid-animation — where its measured height stays exact. The theming guide covers tuning the transition duration on either side.

Accessibility and keyboard

shadcn/ui inherits Radix's keyboard map: Enter and Space toggle a panel, Arrow Up / Down cycle between triggers, and Home / End jump to the first or last trigger. Focus is managed by Radix's internal roving tabindex, so the Tab key skips closed triggers by default in vertical variants.

Drivn's Accordion emits aria-expanded on each trigger and wraps content in a role="region" container, but it does not implement Arrow-key cycling or a roving tabindex. Tab reaches every trigger in document order, and Enter or Space toggles the panel natively because each trigger is a real <button> element.

For most FAQ and settings surfaces this is sufficient and passes WCAG 2.1 AA. If your product has a long list of accordions where Arrow-key navigation is expected — a tree-view pattern, for instance — Radix's richer focus model is the better fit. The theming guide covers how to tune focus-ring styles on either library.

Customization

shadcn encourages overrides via CSS variables and Tailwind's data-state attribute selectors. You can target [data-state="open"] for open-panel styling, edit the keyframes in tailwind.config, or wrap the trigger in a styled shell.

Drivn replaces this with a co-located const styles object. Inside src/components/ui/accordion.tsx — the same file the CLI installs — the styles.trigger, styles.panel, and styles.content strings hold every class name. You edit them directly. The ChevronDown icon can be swapped for any Lucide icon, the padding and spacing adjusted, the border tokens re-tuned. Because Drivn uses HSL color tokens, dark mode tracks automatically. The installation page shows how to regenerate a file from the registry if you want to reset after an experiment.

What Drivn does not have

Drivn's Accordion does not expose imperative open and close methods on a ref, and it does not support a type="single" collapsible={false} variant that forces at least one panel open at all times. shadcn's Radix-based component does. If your design calls for external buttons that pilot specific panels, Drivn asks you to lift the state manually.

Drivn also lacks the Arrow-key navigation roving tabindex described above. For a long FAQ with ten or more items, shadcn's built-in keyboard map is measurably better for power users. For short lists the difference disappears.

Finally, shadcn's ecosystem advantage shows up here too — pre-built FAQ blocks, AI-generated question sets, and community themes exist in numbers Drivn has not matched yet.

Get started

Install Drivn in one command

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

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

Migrate if you want to drop Radix from your dependency tree, prefer the single-import dot-notation API, or want a component file you can read end-to-end. Stay on shadcn if users rely on Arrow-key navigation across long lists, if you open panels imperatively from external refs, or if you benefit from Radix's well-tested focus model. For short FAQ sections the visible behavior is nearly indistinguishable, so the choice usually comes down to how you want your dependency graph to look.

Yes. The panel uses a CSS grid with grid-template-rows that transitions between 0fr (closed) and 1fr (open). The browser reads the child height and interpolates the row accordingly, so content of any size expands smoothly. There is no JavaScript measurement loop, no ResizeObserver, and no configuration required — the default styles that ship with the component handle it.

Yes. Pass the multiple prop to the Accordion root and items become independently toggleable. You can combine it with defaultValue={["a", "b"]} as an array to pre-open a subset on mount. Without multiple, opening one item closes the previously open one automatically, which matches the single-open accordion pattern most FAQ lists use. Switching between modes is a one-character change.

Partially. Tab reaches every trigger in document order and Enter or Space toggles the panel, because triggers are native <button> elements. Drivn does not implement Arrow-key cycling between triggers or a roving tabindex — for that behavior shadcn's Radix-powered Accordion is the stronger choice. For short lists under roughly ten items this distinction is rarely noticed by users or flagged in accessibility audits.

Not directly — the current release stores open state in a local Set inside the root component. If you need external control (URL-driven, Zustand store, or parent state), lift the component body into your own wrapper and pass open plus onOpenChange via props. Because Drivn components live in your repo after install, this refactor is a one-time change inside src/components/ui/accordion.tsx.