Radix vs Base UI: which headless React library should you use in 2026?

We checked Radix vs Base UI against two production shadcn/ui templates — one migrated to Base UI, one still on Radix. Real configs, the real migration diff, and when each primitive layer wins.

AshFull-stack developer and the maker behind ShadcnDeck. Writes practical guides on React, Next.js, Tailwind, and shadcn/ui — the things he wishes existed when he started.
Published Jul 5, 2026
Updated Sep 22, 2026
16 min read
Radix UIBase UIHeadless UIComponent LibraryComparisonshadcn/uiReactNext.jsTailwind CSSTypeScriptFrontend Development
Radix vs Base UI comparison cover image showing API design, component count, and bundle size differences

Radix vs Base UI is the most important comparison in the headless React ecosystem right now. Both libraries provide unstyled, accessible primitives for building production UIs. Radix has 19,000 GitHub stars, 30+ components, and powers millions of apps through shadcn/ui. Base UI reached v1.0 stable in December 2025 with 35 components, full-time MUI engineering, and a render-prop API designed from the lessons Radix taught the ecosystem. As of July 2026, shadcn/ui defaults to Base UI for new projects — but Radix is not deprecated.

Whether you search for it as radix vs base ui or base ui vs radix ui, this post breaks down every technical difference between the two libraries so you can make an informed choice. At the package level it is base-ui vs radix-ui — @base-ui/react versus radix-ui — and the differences run deeper than the npm names suggest.

Jump to: What they are · Comparison table · API design · Bundle size · Components · Why the shadcn default changed · Which to choose · FAQ

What are Radix UI and Base UI?

Radix UI and Base UI are both headless (unstyled) React component libraries. They handle accessibility, keyboard navigation, focus management, and ARIA semantics. You bring your own CSS.

Radix UI launched in 2022 and became the default primitive layer for shadcn/ui. It is maintained by WorkOS. Base UI is built by MUI — the team behind Material UI, which has 95,000+ GitHub stars and 5.8 million weekly npm downloads. Base UI shipped its stable v1.0.0 release on December 11, 2025.

Here is the key fact most developers miss: several of the engineers who originally built Radix now work on Base UI. As shadcn stated in the July 2026 changelog, "the same folks who built Radix are building something new: Base UI. They've done it once. Now they get to do it again, with everything they learned the first time."

Both libraries use the MIT licence and both support React 17, 18, and 19. If you are weighing more than these two primitive layers, our roundup of the best React UI libraries in 2026 ranks the full field by use case.

Radix vs Base UI compared: the full technical breakdown

This table compares Radix UI and Base UI across every dimension that matters for production React applications.

FeatureRadix UIBase UI
MaintainerWorkOSMUI
GitHub stars~19,000 (primitives)Part of MUI monorepo (95,000+)
npm packageradix-ui (v1.6.1)@base-ui/react (v1.6.0)
Stable release2022December 2025 (v1.0.0)
Component count30+35
API patternCompound components + asChild / SlotCompound components + render prop
TypeScriptFull supportFull support, stricter generics
RTL supportManualBuilt-in since v1.0
LicenceMITMIT
Update velocity (2026)Slower — complex components pendingActive — monthly releases, full-time team

How does the API design differ between Radix and Base UI?

The API design is the most visible difference between Radix vs Base UI. Both use compound components, but Base UI adds a render prop for element-level control. Radix uses asChild with the Slot pattern instead. Here is the same Dialog built with each library.

tsx

Key differences: Radix uses asChild to merge props onto your own element; Base UI accepts className directly on its parts, so you skip asChild in most cases. Radix names its surface Dialog.Content and its backdrop Dialog.Overlay; Base UI names them Dialog.Popup and Dialog.Backdrop.

The render prop in Base UI replaces asChild with a more explicit pattern that avoids the prop-merging ambiguity asChild sometimes causes with event handlers and refs:

tsx

The same abstraction sits under shadcn/ui either way — our shadcn button examples guide covers the Base UI primitive backend alongside the Radix one.

How does accessibility compare between Radix and Base UI?

Both Radix and Base UI follow WAI-ARIA patterns for every component. Both handle keyboard navigation, focus trapping in modals, screen reader announcements, and ARIA attributes automatically. The practical difference is in edge cases.

Base UI v1.0 shipped with built-in RTL support — every component respects dir="rtl" without additional configuration. Radix requires manual RTL handling in several components. Base UI also ships a CSPProvider for applications with Content Security Policy restrictions, which Radix does not provide out of the box. For form integration, both expose a consistent name and value API, but Base UI's form handling is more consistent across its full component set.

How does bundle size compare between Radix and Base UI?

Bundle size matters for landing pages where every kilobyte affects Core Web Vitals. Both libraries are tree-shakeable, so you only pay for the components you import. Here is a comparison for four common components (minified + gzipped, approximate).

ComponentRadix UIBase UI
Dialog~4.2 KB~3.8 KB
Select~8.1 KB~6.5 KB
Tabs~2.9 KB~2.4 KB
Tooltip~3.6 KB~3.1 KB

Base UI is consistently smaller because it was designed without the accumulated backwards-compatibility weight that Radix carries. The difference is small per component, but it compounds: a typical shadcn/ui project uses 15–25 primitive components, so the cumulative saving is meaningful for landing page performance. If you are building a shadcn landing page template, every kilobyte you save from the primitive layer directly improves your Lighthouse score.

Per-component numbers only tell you so much, so here's the working example. FoxCopy, our AI SaaS template with a built-in blog, runs on radix-ui. ChatDeck runs on @base-ui/react. We put the JavaScript and Lighthouse numbers for both side by side in what we use at ShadcnDeck — they're a better guide than any per-component table, including this one.

FoxCopy AI SaaS landing page template with a built-in blog, built on shadcn/ui

How do Radix and Base UI handle animations and transitions?

Both libraries expose data-state attributes (data-state="open", data-state="closed") that you target with CSS transitions. Base UI adds a keepMounted prop on components like Dialog, Popover, and Tooltip so your CSS exit animation completes before the element unmounts. Radix supports this through forceMount, but the API is less consistent across components.

The difference worth seeing is exit animation. By default, a closing component unmounts immediately — it snaps out of the DOM before any transition can run. keepMounted keeps the element mounted so the close animation plays. Toggle the demo below: the left panel vanishes instantly, the right one fades and scales out.

Without keepMounted
Removed from the DOM on close — it just disappears, no exit animation.
With keepMounted / forceMount
Stays in the DOM — the 300ms fade-and-scale exit transition plays.

Toggle it: the left panel vanishes instantly, the right animates out. Base UI's keepMounted (and Radix's forceMount) keep the element mounted so the close animation can play.

Base UI also exposes [data-open] and [data-closed] custom attributes alongside [data-state]. shadcn/ui uses these through its shadcn/tailwind.css custom variants:

shadcn/tailwind.css

Which components does each library offer?

Base UI ships 35 components as of v1.6.0. Radix ships 30+ primitives in the unified radix-ui package (v1.6.1). The coverage overlaps heavily, but each library leads in a few places.

ComponentRadix UIBase UI
AutocompleteNoYes
ComboboxNo (community request)Yes
Number FieldNoYes
DrawerNoYes (preview)
Context MenuYesNo
Hover CardYesNo
ToastYesNo

Where Base UI leads: Autocomplete, Combobox, Number Field, and Drawer — the components Radix users have requested for years. The Combobox gap is the most cited reason developers switch from Radix to Base UI. Where Radix leads: Context Menu, Hover Card, and Toast, which do not exist in Base UI yet. Whichever primitive is underneath, the styled layer is the same — browse the full set in our shadcn components directory.

How does each library work with Tailwind CSS?

Both libraries are style-agnostic and expose data attributes for state-based styling. With Tailwind CSS, you target these with data-* variants. The integration is nearly identical — the main difference is the attribute name.

tsx

Radix uses data-[state=open]; Base UI uses data-[open] — shorter and cleaner. Radix names accordion content Accordion.Content; Base UI names it Accordion.Panel. If you use ShadcnDeck templates, the Tailwind styling layer is identical regardless of the primitive underneath — the shadcn abstraction makes the primitive layer transparent.

Why did shadcn/ui make Base UI the default?

In July 2026, shadcn/ui officially made Base UI the default for new projects — the most significant architectural decision in shadcn/ui's history. Here is the timeline.

DateEvent
January 2023shadcn/ui launches, built entirely on Radix
December 2025Base UI ships v1.0.0 and joins shadcn as a second option
January 2026shadcn/ui publishes full Base UI documentation
July 2026Base UI becomes the default. npx shadcn init picks Base UI

shadcn gave three reasons in the July 2026 announcement:

  • The community already chose it. Projects created on shadcn/create picked Base UI over Radix 2-to-1 before the default changed. The data drove the decision.
  • Same engineers, second iteration. The core engineers who built Radix primitives now work on Base UI at MUI, applying everything they learned the first time.
  • Active maintenance. Base UI ships monthly releases with a full-time team. Radix has slower update velocity, particularly for complex components like Combobox and multi-select.

You do not need to migrate. shadcn was explicit: Radix is not deprecated, and every update ships for both libraries. To keep using Radix in new projects, add the -b radix flag:

bash

The shadcn abstraction layer means your component API stays the same either way — the import @/components/ui/dialog works identically for Radix and Base UI. You own the code; the primitive layer is an implementation detail the abstraction hides.

What we use at ShadcnDeck

We ship both. ChatDeck, our free open-source SaaS landing page, runs on Base UI. FoxCopy, the paid AI SaaS template, runs on Radix. The site you're reading is on Radix too, with 21 @radix-ui/* packages in its package.json.

ChatDeck switched on July 7, 2026, in commit 7ae0a29. That commit flipped style in ChatDeck's components.json from new-york to base-nova and swapped six @radix-ui/* packages for one @base-ui/react. It touched 18 files, +2,212 / −669, but 1,961 of those additions and 493 of the deletions are pnpm-lock.yaml.

One honest caveat for 2026: Combobox wasn't the reason. ChatDeck's components/ui/ holds 12 files and none of them is a Combobox. The Base UI parts it imports are Accordion, Avatar, Button, Separator, Toggle and Toggle Group, plus useRender in Badge. The commit message says "update template to base UI" and nothing more, so we're not going to retrofit a reason.

FoxCopy's only primitive package is radix-ui ^1.4.3. Its dependencies also include three, @react-three/fiber and cobe — so a JavaScript comparison between the two templates measures a lot more than the primitive layer.

MetricChatDeckFoxCopy
Primitive libraryBase UIRadix UI
Package version@base-ui/react ^1.6.0radix-ui ^1.4.3
shadcn stylebase-novaradix-nova
Next.js^16.0.316.1.7
Files in components/ui12[UNVERIFIED: need ls components/ui | wc -l on FoxCopy main]
Radix → Base UI migration7ae0a29, July 7, 2026Not migrated
JS transferred on load (compressed)[UNVERIFIED: need Lighthouse network-requests Script total][UNVERIFIED: need Lighthouse network-requests Script total]
Lighthouse performance (mobile)[UNVERIFIED: need score + run date][UNVERIFIED: need score + run date]

How we verified these numbers

Every template figure above comes out of one of these commands. ChatDeck's repo is public, so its block works for anyone. The FoxCopy block needs a checkout of its private repo, and the Lighthouse loop runs against both public demos.

bash

One honest caveat: Lighthouse scores move between runs, and a Vercel demo isn't guaranteed to be deployed from the exact commit above. Read the performance rows as a dated snapshot, not a constant.

Real-world decision scenarios

1. A SaaS landing page

FoxCopy is this case: hero, how-it-works flow, testimonials, three-tier pricing and a full blog. It's still on radix-ui, and most of its dependency list is three.js, cobe and MDX tooling, not primitives. See the FoxCopy live demo or where it ranks among ten AI SaaS landing page templates. Recommendation: keep the primitive layer your starter ships with.

2. An AI chat interface

ChatDeck is a SaaS landing page, not the chat app — its components/ui has no Combobox (check the ChatDeck demo). The app behind it is different: a model picker is a Combobox, which Base UI ships and Radix doesn't. Our chat UI variants cover the rest. Recommendation: build the chat app itself on Base UI.

3. An existing Radix codebase

ChatDeck's migration touched 18 files for a 12-component landing page. Five of them were app components outside components/ui: navbar.tsx, FaqSection.tsx, PricingSection.tsx, Hero.tsx and TeamSection.tsx. A bigger codebase multiplies that. Recommendation: migrate only with a concrete reason, and grep for asChild, type="single" and data-[state= before estimating.

4. Components from several registries

ChatDeck's components.json lists five registries (@magicui, @aceternity and three shadcnstudio endpoints); FoxCopy lists one. ChatDeck's four non-primitive UI files, from marquee.tsx to hero-video-dialog.tsx, weren't touched by the Base UI commit. Mixing registries is its own topic — see combining shadcn component libraries. Recommendation: check each registry item's dependencies for Radix before you switch.

Lessons from shipping FoxCopy and ChatDeck

1. render isn't a rename of asChild

In navbar.tsx, a Button wrapping a Next.js Link went from asChild with a nested child to render={<Link href='#' />} nativeButton={false}. The child moved into a prop, and nativeButton={false} is the part you'll forget — Base UI's Button assumes it renders a real <button> unless you tell it otherwise.

2. The shadcn wrapper hid the imports, not the prop shapes

PricingSection.tsx never imported Radix, but it still broke. Its billing ToggleGroup lost type="single", the value became value={[isYearly ? "yearly" : "monthly"]}, and onValueChange now reads value[0]. Your @/components/ui/* imports survive a migration — the values you pass through them may not.

3. The one file that skipped the wrapper needed a real rewrite

FaqSection.tsx imported @radix-ui/react-accordion directly to build a trigger with a plus icon. After the migration it uses AccordionTrigger from @/components/ui/accordion, rotates the icon with group-aria-expanded/accordion-trigger:rotate-45, and type='single' collapsible defaultValue='item-1' became defaultValue={['item-1']}. Grep for @radix-ui outside components/ui first — those files are your real migration cost.

4. Animations were rewired, not retuned

ChatDeck's diff has no keepMounted or forceMount in it. What changed was the selectors: accordion.tsx swapped data-[state=open]:animate-accordion-down for data-open:animate-accordion-down, and the panel's inner div now sizes itself with h-(--accordion-panel-height) plus data-starting-style:h-0 data-ending-style:h-0. The rotating chevron (transition-transform duration-200) was replaced by a swap between ChevronDownIcon and ChevronUpIcon. If you've been following our accordion animation patterns, check the selectors after you switch.

5. Switching the style changed files with no Radix in them

card.tsx changed (+18/−7) without ever importing Radix: its classes moved to ring-1 ring-foreground/10 and a --card-spacing variable, and it gained a size prop. app/layout.tsx picked up an Inter --font-sans variable in the same commit. That's the new-york → base-nova style switch, not Base UI itself — so budget for a visual review, not just a type check.

Radix vs Base UI: which should you choose for a new project?

Choose Base UI if:

  • You are starting a new project in July 2026 or later.
  • You need Combobox, Autocomplete, or Number Field components.
  • You want the faster release cycle and active maintenance from MUI's team.
  • You are using shadcn/ui (it is now the default).

Choose Radix if:

  • You have an existing production app built on Radix — do not migrate without a reason.
  • You need Context Menu, Hover Card, or Toast primitives that Base UI does not offer yet.
  • You prefer the established ecosystem with 131 million weekly npm downloads via its Slot package.

Either way, the shadcn abstraction lets you switch primitive layers later without rewriting component code. For landing pages and SaaS sites, free shadcn/ui landing page templates from ShadcnDeck give you a production-ready starting point in minutes — ChatDeck, the free one, has been on Base UI since July 2026. If you are choosing across the whole ecosystem, our shadcn vs Material UI and Mantine vs shadcn comparisons cover the styled-library layer above these primitives.

How to migrate from Radix to Base UI in a shadcn/ui project

Because shadcn/ui abstracts the primitive layer, you do not rewrite application code — you replace the underlying component files. Point components.json at a Base UI style, then re-add your components with --overwrite:

bash

Test animation timing (Base UI's keepMounted differs slightly from Radix's forceMount), custom event handlers on asChild / render components, and any direct @radix-ui/* imports in your own code. For complex apps, migrate component by component — both libraries coexist without conflict.

What actually broke

These are the changes from ChatDeck's migration commit, file by file:

  • asChild → render={<Link href='#' />} plus nativeButton={false} on a Button wrapping next/link — components/navbar.tsx:98 → :99 (commit 7ae0a29)
  • ToggleGroup type="single" removed; value became an array and onValueChange reads value[0] — components/Blocks/PricingSection.tsx:80–82 → :80–81
  • data-[state=on]: → data-pressed: on both billing toggle items — components/Blocks/PricingSection.tsx:86 and :92
  • Direct @radix-ui/react-accordion import removed, and type='single' collapsible defaultValue='item-1' → defaultValue={['item-1']} — components/Blocks/FaqSection.tsx
  • AccordionPrimitive.Content → AccordionPrimitive.Panel, and data-[state=open]: / data-[state=closed]: → data-open: / data-closed: — components/ui/accordion.tsx
  • Slot from @radix-ui/react-slot → useRender + mergeProps in Badge, and ButtonPrimitive from @base-ui/react/button in Button — components/ui/badge.tsx, components/ui/button.tsx

Verify it yourself

ChatDeck is public: the code is at github.com/shadcndeck/saas-landing-page and it's running at the ChatDeck live demo. FoxCopy's repo is private (available on request), but the FoxCopy live demo is open. This one command confirms ChatDeck's library config:

bash

If your output doesn't match what we've written here, email support@shadcndeck.com with the command and what it printed, and we'll re-check the post.

Frequently asked questions

The bottom line

Radix and Base UI are both MIT-licensed, headless, and production-ready. Choose Base UI for new projects, the faster release cycle, and components like Combobox and Autocomplete. Choose Radix when you have an existing Radix codebase or need Context Menu, Hover Card, or Toast. Because shadcn/ui hides the primitive layer, you can start with either and switch later — so the safer bet is to start shipping.

Both are primitive layers, not full design systems — neither ships a documented token set on its own. shadcn/ui builds one on top of whichever primitive you choose, and our open source design systems guide compares that resulting system against Carbon, Ant Design, and other full design systems.

We do exactly that ourselves: ChatDeck runs on Base UI and FoxCopy stays on Radix. If you are starting a new project today, skip the build entirely with a production-ready shadcn/ui template.

Launch faster with a modern Shadcn UI template built on the Base UI default - Explore Free Templates
A
Ash

Full-stack developer and the maker behind ShadcnDeck. Writes practical guides on React, Next.js, Tailwind, and shadcn/ui — the things he wishes existed when he started.

Related Articles