Why Base UI, Not Radix
Because the people who built Radix said so.
The backstory
Radix UI dominated React component libraries for three years. 18,000 stars. 9.6 million weekly downloads. shadcn built their entire component system on it.
Then Colm Tuite — co-creator of Radix — left the project and joined the Base UI team, bringing the lessons learned from Radix with him.
That's not a market signal. That's the architect leaving the building and moving into a new one.
What happened
Colm Tuite, co-creator of Radix UI, joined forces with the Floating UI team (Atomiks) and the MUI team. Together they built @base-ui/react — a clean-room rewrite of headless React primitives.
Base UI isn't a Radix fork. It's what Radix would be if they started over knowing everything they know now. And for toasts specifically, the difference is not subtle.
The Radix Toast problem
Radix Toast is declarative-only. Assemble it yourself:
<Toast.Provider>
<Toast.Root>
<Toast.Title>...</Toast.Title>
<Toast.Description>...</Toast.Description>
<Toast.Action>...</Toast.Action>
<Toast.Close>...</Toast.Close>
</Toast.Root>
<Toast.Viewport />
</Toast.Provider>No toast.success(). No imperative API. No createToastManager(). You can't call it from an event handler, a server action, or anywhere outside a React component tree without building your own state management.
This is why shadcn deprecated their Radix toast wrapper and replaced it with Sonner. When the most influential React component library gives up on your toast component, that's a sign.
What Base UI Toast provides
import { Toast } from "@base-ui/react";
const manager = Toast.createToastManager();
// Works anywhere — event handlers, server actions, utilities
manager.add({ title: "Hello", type: "success" });Imperative. Singleton. Works anywhere. Plus a reactive store with memoized selectors, proper cleanup on close, Floating UI positioning, CSS transitions via data-starting-style, ARIA priority system, and F6 keyboard navigation.
The foundation does the hard part. No Observer hacks. No setTimeout + flushSync workarounds.
Feature comparison
Toast-specific capabilities. This isn't close.
| Feature | Base UI Toast | Radix Toast |
|---|---|---|
| Imperative API | createToastManager() | None |
| toast.promise() | Built-in | None |
| toast.update() | Built-in | None |
| Anchored positioning | Floating UI built-in | None |
| Arrow element | Toast.Arrow | None |
| ARIA priority | "high" / "low" | Fixed |
| Keyboard nav | F6 (ARIA standard) | Custom |
| CSS transitions | data-starting-style / data-ending-style | data-state only |
| Height tracking | CSS variable (--toast-height) | Manual JS |
| Stacking variables | --toast-index, --toast-frontmost-height | None |
| Swipe handling | Native, accessible | Custom pointer events |
| Close all | manager.close() | Manual |
| React 19 | Full support | Partial |
| Last release | Feb 2026 (v1.2.0) | 6+ months ago |
| Development | Weekly commits | Maintenance mode |
The shadcn signal
shadcn now supports both Radix and Base UI components. Their docs include Base UI variants. They deprecated their own Radix toast wrapper — replaced it with Sonner, which is itself a from-scratch implementation because Radix Toast wasn't enough.
When the ecosystem's most influential component library starts hedging between Radix and Base UI, you pick the one that's actively developed by the people who made both.
Development velocity
Base UI ships monthly. The toast primitives alone have had significant improvements. Here are the PRs that directly benefit Popser:
Radix Toast's last meaningful update was over six months ago.
Why not just use Base UI directly?
You can. It's a great library. But Base UI gives you LEGO bricks, not a toast. You'd need to:
That's what Popser is. The assembly already done. Same Base UI primitives underneath, but ready to use in five lines instead of fifty.
import { toast, Toaster } from "@vcui/popser";
import "@vcui/popser/styles";
// That's it.
toast.success("Done.");