shadcn/ui drops Radix for Base UI — the primitive layer war just tilted

4 min read 1 source clear_take
├── "The swap is a pragmatic follow-through, not a betrayal — the primitive layer's center of gravity moved and shadcn moved with it"
│  └── top10.dev editorial (top10.dev) → read below

The editorial frames Base UI as the natural successor to Radix Primitives, built by ex-Radix and ex-MUI engineers with MUI's backing, while Radix itself has been on maintenance-only footing for months. Given that the Radix team is publicly folding effort into Base, shadcn's decision to default new components to Base UI is characterized as tracking upstream reality rather than picking a fight.

└── "Swapping the default primitive is effectively a silent ABI break for thousands of production apps"
  ├── top10.dev editorial (top10.dev) → read below

The editorial argues that because shadcn copies source into consumers' repos, Radix became an invisible load-bearing dependency for a huge slice of the React ecosystem. Base UI's contracts are close but not identical — slot patterns, the `render` prop vs `asChild`, and controlled/uncontrolled state exposure all diverge in subtle ways — so anyone who treated shadcn output as stable copy-paste is about to discover they were coding against Radix's ABI without noticing.

  └── @dabinat (Hacker News, 110 pts) → view

By surfacing the changelog entry on HN with the framing 'Shadcn/UI now defaults to Base UI instead of Radix,' the submitter highlights the quiet, consequential nature of the switch. The implicit position is that a default-primitive change deserves loud attention precisely because so many production apps inherited Radix semantics through shadcn without ever choosing them explicitly.

What happened

The shadcn/ui changelog quietly flipped a switch that anyone maintaining a React design system should read twice. New components are now generated on top of Base UI instead of Radix UI, the headless primitive library that shadcn has ridden since day one. Existing Radix-based components still work; the CLI just stops reaching for `@radix-ui/*` when you scaffold new ones and starts pulling `@base-ui-components/react` instead.

For context: shadcn/ui isn't a package you install — it's a CLI that copies component source into your repo. That model made Radix the invisible load-bearing dependency for a huge slice of the React ecosystem. Every Dialog, Popover, Select, and Dropdown that got `npx shadcn add`-ed inherited Radix's accessibility work, focus traps, and portal semantics. Swapping the default primitive isn't a cosmetic change — it's a rewrite of the contract that thousands of production apps have been coding against.

Base UI itself isn't a hostile fork. It's built largely by ex-Radix and ex-MUI engineers, backed by MUI, and explicitly positioned as the successor to Radix Primitives (which has been on maintenance-only footing for months, with the Radix team publicly folding effort into Base). So shadcn moving the default is less "betrayal" and more "the primitive layer's center of gravity moved, and shadcn moved with it."

Why it matters

The first-order effect is API drift. Base UI's component contracts are close to Radix but not identical: slot patterns, `render` prop conventions, and how controlled/uncontrolled state is exposed all differ in ways that look small in a diff and large in a bug tracker. If you've written wrapper components that lean on Radix's `asChild` pattern, the Base UI equivalent (`render`) is philosophically similar but not a drop-in. Anyone who treated shadcn components as "stable copy-paste output" is about to learn they were actually depending on Radix's ABI without noticing.

The second-order effect is more interesting: this is the clearest signal yet that the headless primitive market has consolidated. For two years the story was Radix vs. Headless UI vs. React Aria vs. Ariakit, with shadcn implicitly picking Radix and dragging the ecosystem with it. Radix's slowdown, MUI's decision to bankroll Base UI, and now shadcn defaulting to Base collapse three of those brands into one lineage. React Aria (Adobe) and Ariakit are now the only serious independent alternatives, and both target different audiences — React Aria for teams that want Adobe-grade a11y guarantees, Ariakit for teams that want a smaller surface area.

Community reaction on the HN thread has been muted-but-nervous rather than celebratory. The recurring worry isn't Base UI's quality — it's the *velocity* of change in a layer that was supposed to be boring. One commenter put it bluntly: "I picked shadcn because Radix felt like it was going to still exist in five years. Now I'm learning a new API before I've finished the first migration." That's the practitioner tax nobody costs into the "just copy the component" pitch.

There's also a governance angle worth naming. shadcn/ui is effectively a single-maintainer project (with Vercel's backing) that sets defaults for a huge number of downstream teams. When one person's CLI default becomes the de facto React primitive layer, ecosystem stability is a function of that person's judgment calls, not a standards process. That's not a criticism — the judgment calls have been good — but it's the actual architecture of decision-making here, and pretending otherwise is how teams get surprised.

What this means for your stack

If you're on shadcn today, nothing broke this morning. Your existing components still import from `@radix-ui/*` and will keep working. But three things are now true that weren't yesterday:

First, new component adds will mix primitives. Run `npx shadcn add dialog` today and you'll get Radix; run it on a fresh install tomorrow and you'll get Base. If your team pulls new components in ad-hoc, you'll end up with a codebase that imports from both `@radix-ui/react-dialog` and `@base-ui-components/react/dialog`, doubling your bundle's primitive footprint and your team's mental model. Pin the version of shadcn/ui your CLI uses, or standardize the migration explicitly.

Second, your abstraction layer is now a migration project, not a config change. Any team that built internal wrappers on top of shadcn — a `` that composes shadcn's `

Hacker News 270 pts 146 comments

Shadcn/UI now defaults to Base UI instead of Radix

→ read on Hacker News
ricardobeat · Hacker News

It’s getting tiring to hear at this point, but the Claude voice comes through clearly in this post, and it’s a little off-putting. If even an important product release doesn’t warrant full human attention, where will we draw the line?

notnullorvoid · Hacker News

It bothers me that these popular UI toolkits render copious amounts of divs. I went to look at Base UI and for nearly every component it's "Renders a <div> element", when there are native elements that can achieve the same thing like details/summary for accordion.I'm f

chvid · Hacker News

For boring applications - do people prefer the copy paste approach of shadcn instead of a traditional ui library like mantine?The copy paste approach may be easily modifiable but creates new problems - ie now there is an upgrade ai agent for something that should just be ticking up a version number.

ahmedehab_01 · Hacker News

Would that lead to anything better for the users, such as a smaller bundle size? Or is it more of a QoL change for Shadcn maintainers?

gherkinnn · Hacker News

I have used and mostly like Shadcn, and yet their Radix-based radio button was a bit much, as are other choices, where similarly overblown solutions were used.https://news.ycombinator.com/item?id=46688971

// share this

// get daily digest

Top 10 dev stories every morning at 8am UTC. AI-curated. Retro terminal HTML email.