Skip to content
Drivn logoDrivn
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.

Drivn's Accordion and shadcn/ui's Accordion both arrive in your codebase as editable TypeScript files rather than npm modules. That is the end of the resemblance. shadcn's component is a styled wrapper around Radix UI's Accordion primitive, which brings WAI-ARIA compliance, roving tabindex, and precise animation timing as well-tested runtime dependencies. Drivn's Accordion contains zero third-party UI code — state lives in a React Set, the chevron comes from lucide-react, and the panel animates via a CSS grid-template-rows transition between 0fr and 1fr.

For a simple FAQ section this engineering difference is invisible to your users. For teams that audit every runtime dependency, measure bundle impact at the component level, or want to reason about what their accordions do byte-by-byte, it matters. shadcn inherits Radix's industrial keyboard model plus roughly six kilobytes of primitives; Drivn ships a single file you can read in thirty seconds and adjust without any framework context.

This page breaks the comparison into API shape, animation technique, accessibility coverage, customization surface, and what Drivn has chosen not to implement. Every section shows code from both sides so you decide on what ships in your bundle, not on what a homepage promises.

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

shadcn/ui uses Radix's data-state="open" and data-state="closed" attributes paired with accordion-down and accordion-up Tailwind keyframes. The height is measured via a CSS variable Radix injects, which enables smooth JavaScript-free transitions even for content of unknown size.

Drivn's animation is simpler: the accordion panel is a CSS grid with a grid-template-rows value that transitions from 0fr (closed) to 1fr (open). The browser interpolates the fractional row height automatically, so no JavaScript measurement, no ResizeObserver, and no CSS variable injection is needed. One transition-[grid-template-rows] class handles dynamic content height without configuration.

Neither approach janks at typical sizes — both hit 60fps on normal FAQ content. Drivn wins on simplicity; Radix wins on well-tested fallback behavior for unusual content (very tall, very short, or images that finish loading after the open transition).

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.