shadcn/ui switches its default from Radix to Base UI—what it means and how to migrate safely
A Friday deploy, a surprising change
Picture this: you’ve been happily using shadcn/ui for months, and your team’s UI code feels stable. Then, after an update, your component library “defaults” change—shadcn/ui now defaults to Base UI instead of Radix. For many developers, the first instinct is worry: Did behavior change? Did accessibility change? Will the UI break?
The good news is that this kind of switch is usually engineered to keep your app working, while nudging new projects toward a different underlying component set. Still, “default changed” is not something to ignore. Let’s unpack what it means, why it’s happening, and the practical steps to migrate with confidence.
The cast of characters: shadcn/ui, Radix, and Base UI
Before the migration story makes sense, we need to name the roles.
- shadcn/ui is a collection of React component implementations (buttons, dialogs, selects, tables, etc.) with consistent styling and APIs.
- Radix UI is a popular library of headless (unstyled) accessibility-focused components such as dropdown menus and dialogs.
- Base UI is another headless component library built by a team with prior experience from Radix.
A key beginner-friendly idea is that “component libraries” often sit underneath. They provide the behavior (keyboard support, focus management, ARIA attributes), while shadcn/ui focuses on a cohesive look and a consistent API shape.
So when defaults switch, the question becomes: which underlying behavior library do new shadcn/ui components pull from? The app’s visuals might remain the same, but the behavior wiring can differ.
Why switch defaults at all? Stability over hype
From a product perspective, switching isn’t done lightly. The changelog’s narrative highlights a recurring truth in UI engineering: switching component libraries in production is risky.
The “worst thing you can do” argument isn’t fearmongering—it’s about the real failure modes:
- Subtle behavior differences (focus order, default open/close timing)
- Edge-case accessibility differences (keyboard navigation, screen reader labels)
- Integration mismatches (custom wrappers, portals, or form wiring)
Instead of forcing a breaking switch, the strategy described is more like a careful refactor:
- shadcn/ui rebuilds every component to support the new library’s behavior.
- shadcn/ui keeps the same abstraction (so the way you use components doesn’t radically change).
- You can choose which underlying library to use.
- New projects get a new default once Base UI is considered stable.
That timeline—beta, docs maturation, and then default change—is how teams earn confidence for the default to move.
“Defaults changed” — what you should actually check
When a default changes, it usually affects one or both of these:
- How new components are generated (for example, via a CLI that scaffolds a project)
- What library shadcn/ui imports or expects behind the scenes
For existing projects, the impact depends on whether your setup pins a choice explicitly.
What tends to stay safe
In well-structured ecosystems, the following tends to remain stable:
- Your existing component usage (e.g.,
<Button />,<Dialog />,<Select />) continues to compile. - Your styling system (theme tokens, class names) remains consistent.
What can still bite
Even with “same abstraction,” there are things that can shift:
- Peer dependency resolution: the updated dependency graph might bring different versions.
- Behavior around complex components: dialogs, dropdown menus, and comboboxes are where focus/keyboard wiring matters most.
- Integration code: custom wrappers around Radix-specific patterns, if any, may need attention.
So the safe approach is to treat this as a migration-lite: verify behavior in the areas most likely to differ.
Migration strategy: move in layers
The goal is to preserve confidence while still adopting the new default for new work.
Step 1: Audit your shadcn/ui setup
Look for where your project chooses the underlying component library. Many ecosystems let you configure this either via project config files, generator options, or imports.
In shadcn/ui-style setups, the most common “tell” is how components are imported or configured in your app. Search your repo for hints such as:
- references to Radix or Base UI packages
- generator config files
- shadcn/ui CLI commands used in your build pipeline
If your project explicitly selects Radix, keep doing that until the end-to-end tests pass.
Step 2: Pin versions during the transition window
When defaults change, version drift can obscure root causes.
A conservative practice is:
- Update dependencies with lockfiles intact
- Prefer a single dependency update at a time
This reduces the odds that you’re debugging multiple changes simultaneously.
Step 3: Smoke-test the “behavior hot spots”
Components that typically stress the accessibility and interaction model are:
- Dialogs and Drawers (focus trapping, portal mounting)
- Dropdown menus and Selects (keyboard navigation and typeahead)
- Comboboxes and Command-style inputs (aria roles and selection behavior)
A practical way to do this is to run through real interaction flows:
- Open with keyboard (Tab / Enter / Space)
- Navigate options with arrow keys
- Verify the correct element is focused when the component opens/closes
- Verify closing behavior on Escape and outside click
This is where differences between Radix and Base UI would most likely appear.
A tiny demo mindset: why “unstyled headless” still changes behavior
To understand why switching libraries can matter, it helps to imagine a split architecture:
- Visual layer: CSS classes, layout, colors
- Interaction layer: keyboard handlers, focus management, ARIA attributes
Even if your visuals look identical, the interaction layer might not be.
Think of it like switching the engine under the same car body. The dashboard can look the same while the steering response changes slightly.
In React UI libraries, headless components often control the interaction layer and provide structure. shadcn/ui then styles that structure. When the headless layer changes from Radix to Base UI, the styled components can still be consistent—but you should validate interactions.
What new projects gain from the Base UI default
The most compelling reason to accept a new default is that it signals readiness. The changelog highlights:
- Base UI reaching a stable version (1.6.0)
- a meaningful usage footprint (multi-million weekly downloads)
- continued component growth from the same team that previously built Radix
Practically, this gives you:
- a newer default that likely fits current shadcn/ui component implementations
- fewer “migration paths” for teams starting greenfield work
- a standard direction for future component updates
Potential gotchas and how to reason about them
Two classes of gotchas show up in migrations like this.
1) Custom wrappers that depended on Radix internals
If your code reaches into behaviors or expects a certain DOM structure, the underlying library swap can affect it. The safest assumption is that shadcn/ui maintains abstraction, but custom code might not.
Search for code that customizes:
- focus behavior
onOpenChangehandling patterns- portal/container targets
- aria labeling
2) Testing gaps
Keyboard and screen-reader behaviors are often untested in day-to-day development. This default switch is a good moment to run the “hard” paths, because they’re the ones least likely to be covered by normal click-through testing.
Conclusion: treat it as a controlled switch, not a panic switch
shadcn/ui defaulting to Base UI instead of Radix is best understood as a maturation step: Base UI is now stable enough that new projects should start there, while existing projects can keep their chosen underlying library until validation is complete.
The right mental model is layered: visuals can remain consistent, but interaction wiring and accessibility details belong to the underlying headless library. That’s why migration success depends on targeted behavior testing—dialogs, selects, dropdowns, comboboxes—rather than just checking that the UI “looks right.”
Comments (0)
No comments yet. Be the first to respond!
Leave a Comment
Your comment will be visible after review.