Skip to content
Drivn logoDrivn
4 min read

Drivn vs shadcn/ui — Button Component Compared

Side-by-side comparison of the Drivn and shadcn/ui React Button components — runtime deps, bundle size, API style, variants, and accessibility.

Both Drivn and shadcn/ui distribute their React components as copy-paste source code that lives in your repo. That is where the similarity ends. shadcn/ui's Button is a thin wrapper around Radix's Slot primitive with class-variance-authority for variant classes and clsx for merging. Drivn's Button is pure React plus Tailwind — no Radix, no cva, no clsx wrapper — with a const styles object holding every variant.

If you are choosing between the two for a new project or considering a migration, the practical differences are concrete: Drivn ships fewer runtime dependencies, a smaller per-component footprint, and a simpler variant API at the cost of a smaller ecosystem and fewer existing integrations. shadcn/ui has an enormous community with pre-built blocks, admin dashboards, and third-party integrations; Drivn does not, yet.

This page walks through each dimension — API shape, bundle footprint, customization story, accessibility model, and what Drivn does not do yet — with code on both sides so you can decide based on what you actually ship, not on which library has more GitHub stars or a longer sponsor list on the homepage.

Side-by-side comparison

FeatureDrivnshadcn/ui
Runtime UI dependenciesNone (React + Tailwind)@radix-ui/react-slot, cva, clsx
Variant APIconst styles + keyof typeofclass-variance-authority (cva)
Icon propleftIcon / rightIcon (component)children (JSX element)
Loading prop
Polymorphic (asChild)
AccessibilityNative button elementRadix Slot + native
LicenseMITMIT
Copy-paste install
TypeScriptTyped via keyof stylesTyped via VariantProps<typeof>
Next.js App RouterWorks out of the boxWorks out of the box

API side-by-side

The two buttons converge on the same usage site: <Button variant="secondary">Click</Button>. Underneath, shadcn's Button uses cva to define variants and asChild to swap the rendered element via Slot. Drivn defines variants as a plain object indexed by variant and size, and renders a native <button> (see the Button docs).

For most buttons this is indistinguishable. The divergence shows up when you want to pass an icon or a loading state — Drivn accepts leftIcon={Plus} and loading={true} as first-class props, whereas shadcn expects you to embed icons and loading spinners as children.

1// shadcn/ui
2<Button variant="secondary">
3 <Plus className="mr-2 h-4 w-4" /> New item
4</Button>
5
6// Drivn
7<Button variant="secondary" leftIcon={Plus}>
8 New item
9</Button>

Bundle size and runtime deps

shadcn/ui's Button imports @radix-ui/react-slot (~1.2 kB gzipped) plus class-variance-authority (~0.8 kB) plus clsx (~0.3 kB). Drivn's Button imports nothing beyond React and your cn() utility (which you likely already have). On a page with ten different components, the compounding difference is meaningful — tabs, dialog, and others in Drivn all skip these primitives.

None of this changes what ships in your node_modules. Both libraries copy source into your repo. The difference is what your final bundle pulls in when Next.js tree-shakes — Drivn pulls fewer modules because it imports fewer modules.

Customization

shadcn recommends extending variants via cva's cn() merge or by editing the buttonVariants function. Drivn recommends editing the styles object directly — add a key to variants, add a key to sizes, or change a class. Because the types read from keyof typeof styles, autocomplete updates on save.

For teams that prefer the cva pattern, shadcn will feel familiar. For teams that want fewer layers between "render a button" and the final markup, Drivn removes one level of indirection. Neither approach is objectively better — the question is which mental model matches your team.

What Drivn does not have

Drivn is younger and smaller than shadcn/ui. The ecosystem around shadcn — blocks, examples, community themes, third-party integrations — is larger by orders of magnitude. If you need a ready-made admin dashboard or marketing template built on your component library today, shadcn has more starting points.

Drivn also does not support asChild / polymorphic rendering. If your design system relies heavily on rendering the same props as different underlying elements (e.g., a button that is really a link), shadcn's Radix Slot pattern is more ergonomic. In Drivn you render a link and style it like a button, which some teams prefer for clarity.

Migrating a Button from shadcn to Drivn

Rename <Button asChild><Link /></Button> to <Link className={buttonClasses}>. Replace icon children with leftIcon / rightIcon props. Remove cva imports; Drivn does not need them. The surrounding <form>, <Dialog>, or <Tabs> markup stays the same — only the Button changes. See the installation guide for the CLI command.

For teams that use shadcn's buttonVariants() helper to apply button styles to other elements (typically <Link>), the Drivn equivalent is to extract the styles object from the Button source and re-use it via cn(). Since the Button source now lives in your repo, the extraction is a one-time refactor. Most migrations touch 20-50 button call sites and take under an hour per app. Test deep-interaction flows (form submits, confirm dialogs) carefully — the loading prop replacement saves more code than it breaks.

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

Only if you have concrete reasons — smaller bundle, preference for the dot notation API, or wanting to drop Radix from your dependency graph. If shadcn works well for your team and you are using its asChild pattern heavily, staying put is often the right call. Drivn is a better fit for teams starting fresh or replacing a handful of components at a time.

Yes. Drivn's Button renders a real <button> element with correct type, disabled, and aria-busy handling. Keyboard focus, native Enter/Space activation, and form submission work because the browser implements them on native elements. Drivn does not use Radix for accessibility — it relies on HTML semantics and adds ARIA only where needed.

Technically yes — both install into src/components/ui/ and both are copy-paste. You can gradually replace one with the other. In practice, maintaining two parallel component libraries creates style inconsistencies and doubles your bundle. Pick one and stick with it for the component families you care about.

Yes. The Button, like most Drivn primitives, works as a Server Component import unless it uses state or browser APIs. Components that need hooks are marked with "use client" at the top of the file — you can see which is which by opening the source in your repo. No wrapper or boundary configuration is needed.

The large shadcn ecosystem: third-party blocks, templates, community themes, and pre-built integrations. Drivn's library is smaller and newer. You also lose asChild polymorphism, which some teams use heavily for rendering links and menu items. For a greenfield project or a small surface area, the trade is usually acceptable.