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.
| Feature | Radix UI | Base UI |
|---|---|---|
| Maintainer | WorkOS | MUI |
| GitHub stars | ~19,000 (primitives) | Part of MUI monorepo (95,000+) |
| npm package | radix-ui (v1.6.1) | @base-ui/react (v1.6.0) |
| Stable release | 2022 | December 2025 (v1.0.0) |
| Component count | 30+ | 35 |
| API pattern | Compound components + asChild / Slot | Compound components + render prop |
| TypeScript | Full support | Full support, stricter generics |
| RTL support | Manual | Built-in since v1.0 |
| Licence | MIT | MIT |
| Update velocity (2026) | Slower — complex components pending | Active — 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.
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:
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).
| Component | Radix UI | Base 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.
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.
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:
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.
| Component | Radix UI | Base UI |
|---|---|---|
| Autocomplete | No | Yes |
| Combobox | No (community request) | Yes |
| Number Field | No | Yes |
| Drawer | No | Yes (preview) |
| Context Menu | Yes | No |
| Hover Card | Yes | No |
| Toast | Yes | No |
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.
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.
| Date | Event |
|---|---|
| January 2023 | shadcn/ui launches, built entirely on Radix |
| December 2025 | Base UI ships v1.0.0 and joins shadcn as a second option |
| January 2026 | shadcn/ui publishes full Base UI documentation |
| July 2026 | Base 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:
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.
| Metric | ChatDeck | FoxCopy |
|---|---|---|
| Primitive library | Base UI | Radix UI |
| Package version | @base-ui/react ^1.6.0 | radix-ui ^1.4.3 |
| shadcn style | base-nova | radix-nova |
| Next.js | ^16.0.3 | 16.1.7 |
Files in components/ui | 12 | [UNVERIFIED: need ls components/ui | wc -l on FoxCopy main] |
| Radix → Base UI migration | 7ae0a29, July 7, 2026 | Not 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.
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:
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='#' />}plusnativeButton={false}on a Button wrappingnext/link—components/navbar.tsx:98→:99(commit7ae0a29)- ToggleGroup
type="single"removed;valuebecame an array andonValueChangereadsvalue[0]—components/Blocks/PricingSection.tsx:80–82→:80–81 data-[state=on]:→data-pressed:on both billing toggle items —components/Blocks/PricingSection.tsx:86and:92- Direct
@radix-ui/react-accordionimport removed, andtype='single' collapsible defaultValue='item-1'→defaultValue={['item-1']}—components/Blocks/FaqSection.tsx AccordionPrimitive.Content→AccordionPrimitive.Panel, anddata-[state=open]:/data-[state=closed]:→data-open:/data-closed:—components/ui/accordion.tsxSlotfrom@radix-ui/react-slot→useRender+mergePropsin Badge, andButtonPrimitivefrom@base-ui/react/buttonin 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:
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.
Related Posts
- 12 shadcn accordion patterns - including the open/close animations that changed selectors in this migration
- 21 shadcn component libraries - the registries you'll pull from, and how to run several at once
- AI SaaS landing page templates, ranked - FoxCopy alongside nine alternatives
- free shadcn landing page templates - twelve no-cost starters, ChatDeck among them
- shadcn chat UI examples - message lists and inputs for the app a model picker lives in
- shadcn button variants - the component whose
asChildchanged first - the shadcn/ui component hub - every component guide we've published, in one place
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.





