Skip to content
Drivn
5 min read

Drivn vs shadcn/ui — Toast Compared

Drivn and shadcn/ui both build their Toast on Sonner. Compare the pre-wired lucide icons, card-token theming, and the toast() API side by side.

Here is the honest fact that frames this whole comparison: Drivn and shadcn/ui build their Toast on the same library. Both wrap Sonner — the headless toast library by Emil Kowalski — so the underlying queue, stacking, swipe-to-dismiss, and timing logic are identical on both sides. shadcn/ui deprecated its old Radix-based Toast and now ships a Sonner component; Drivn ships a Toaster that does the same job. This is not a "different engine" story, it is a "different defaults" story, and the defaults are where Drivn earns its place.

Drivn's Toast lives at packages/drivn/src/registry/components/toast.ts. It is a thin Toaster wrapper that pre-wires five lucide-react icons — one each for success, info, warning, error, and a spinning loader — and themes the toast surface with Drivn's own --card, --card-foreground, and --border design tokens plus a 12px radius. The toast() function is re-exported straight from Sonner, so toast.success, toast.error, and friends work exactly as the Sonner docs describe. This page compares the two wrappers with verbatim source from the Drivn registry, and is honest about how thin the gap is and where it still matters.

Side-by-side comparison

FeatureDrivnshadcn/ui
Underlying librarySonnerSonner (old Radix toast deprecated)
Install commandnpx drivn add toastnpx shadcn add sonner
Built-in type iconsYes — 5 lucide icons pre-wiredNo — Sonner defaults, no icon map
Theming approachcard/border design tokens via CSS varsnext-themes theme prop
Border radius12px set in the ToasterInherits Sonner default
Loading spinnerLoader2 with animate-spinSonner default loader
toast() APIsuccess / error / info / warning / loadingSame — re-exports Sonner
Runtime dependenciessonner + lucide-reactsonner + next-themes
Styling patternInline style object on the ToasterclassName with cn() + theme prop
LicenseMITMIT

Both Drivn and shadcn build Toast on Sonner

The starting point of any honest comparison: this is not Drivn's queue versus shadcn's queue. shadcn/ui retired its original Radix-primitive Toast and now recommends a Sonner component that renders <Toaster /> from the Sonner library. Drivn does the same — its Toast is a Toaster that mounts Sonner once near your app root. So stacking behaviour, swipe-to-dismiss, auto-dismiss timers, the promise API, and accessibility live-region announcements are identical, because they come from the same upstream package on both sides.

What that means in practice is that you cannot pick one of these for a fundamentally better notification engine — there isn't one. You pick based on the wrapper's defaults: which icons are wired, how the surface is themed, and how cleanly the install drops into your stack. The rest of this page is about exactly those defaults, copied verbatim from the Drivn registry so you can see precisely what npx drivn add toast gives you over a bare Sonner install.

1import { Toaster, toast } from "@/components/ui/toast"
2
3// Mount once near the app root, then call toast() anywhere
4<Toaster />
5toast.success("Changes saved")

Drivn pre-wires five lucide icons

The most visible difference is the icon map. A bare Sonner install shows no leading icon on typed toasts unless you wire one yourself. Drivn's Toaster passes an icons prop to Sonner that maps each toast type to a lucide-react icon: CheckCircle2 for success, Info for info, AlertTriangle for warning, XCircle for error, and a Loader2 spinning at animate-spin for the loading state. Every icon is sized w-4 h-4 so it sits consistently against the text.

This is the block you would otherwise hand-author against the Sonner API. Because Drivn ships it pre-wired and you own the source after install, you can swap any icon for your own brand set by editing the one icons object — no fork, no config layer. The snippet below is copied verbatim from the registry.

1// packages/drivn/src/registry/components/toast.ts — verbatim
2<Sonner
3 icons={{
4 success: (
5 <CheckCircle2 className="w-4 h-4" />
6 ),
7 info: (
8 <Info className="w-4 h-4" />
9 ),
10 warning: (
11 <AlertTriangle className="w-4 h-4" />
12 ),
13 error: (
14 <XCircle className="w-4 h-4" />
15 ),
16 loading: (
17 <Loader2 className="w-4 h-4 animate-spin" />
18 ),
19 }}
20/>

Theming through card and border tokens

The second difference is how the toast surface is themed. shadcn's Sonner wrapper leans on the theme prop from next-themes plus a set of toastOptions classNames. Drivn instead drives the surface entirely from its own design tokens through Sonner's CSS custom properties: --normal-bg is set to var(--card), --normal-text to var(--card-foreground), --normal-border to var(--border), and --border-radius to 12px. Because those tokens already flip between light and dark in Drivn's theme system, the toast follows your theme without a separate theme prop.

The practical payoff is that a Drivn toast matches a Drivn Card out of the box — same background, same border colour, same corner radius — so notifications feel like part of the same surface family rather than a bolted-on library default. The style block below is verbatim from the registry.

1// packages/drivn/src/registry/components/toast.ts — verbatim
2style={{
3 '--normal-bg': 'var(--card)',
4 '--normal-text': 'var(--card-foreground)',
5 '--normal-border': 'var(--border)',
6 '--border-radius': '12px',
7}}

The toast() API is identical — by design

Because Drivn re-exports toast directly from Sonner (export { Toaster, toast }), every call you know from the Sonner or shadcn docs works unchanged. toast.success, toast.error, toast.info, toast.warning, and toast.loading all fire the matching typed toast with the pre-wired icon. The second argument carries options — a description string for a secondary line, an action object with a label and onClick for an inline button, and toast.promise for tying a toast to an async lifecycle.

This is deliberate: Drivn does not invent a new notification API on top of Sonner, it just gives you better defaults underneath the same calls. So migrating a shadcn Sonner setup to Drivn is mechanical — swap the import path, keep every toast() call. For the full set of usage patterns, the Toast examples page walks the variants, descriptions, actions, and positioning one at a time.

1toast.success("Published!", { description: "Your post is now live." })
2
3toast.success("File deleted", {
4 action: {
5 label: "Undo",
6 onClick: () => restoreFile(),
7 },
8})
Get started

Install Drivn in one command

Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.

npx drivn@latest create

Requires Node 18+. Works with npm, pnpm, and yarn.

Enjoying Drivn?
Star the repo on GitHub to follow new component releases.
Star →

Frequently asked questions

Effectively yes — Drivn's Toast is a thin Toaster wrapper around Sonner that re-exports the toast function unchanged. What Drivn adds on top is a pre-wired set of five lucide-react icons for the success, info, warning, error, and loading states, plus theming that maps Sonner's CSS variables to Drivn's card and border design tokens with a 12px radius.

Yes. shadcn/ui deprecated its original Radix-primitive Toast component and now ships a Sonner component that renders the Toaster from the same Sonner library Drivn uses. So both libraries share the identical underlying queue, stacking, swipe-to-dismiss, and timing behaviour. The differences are limited to the wrapper defaults — icons, theming approach, and install command.

Drivn passes an icons map to Sonner with five lucide-react icons: CheckCircle2 for success, Info for info, AlertTriangle for warning, XCircle for error, and Loader2 with an animate-spin class for the loading state. Each is sized w-4 h-4. Because you own the source after install, you can swap any of them for your own icon set by editing the single icons object.

Through Sonner's CSS custom properties set on the Toaster style object: --normal-bg maps to var(--card), --normal-text to var(--card-foreground), --normal-border to var(--border), and --border-radius to 12px. Because those tokens already switch between light and dark in Drivn's theme system, the toast follows your theme automatically and matches a Drivn Card without a separate theme prop.

Yes. Pass an action object as the second argument with a label and an onClick handler, for example toast.success("File deleted", { action: { label: "Undo", onClick: () => restoreFile() } }). This is the standard Sonner action API, which Drivn inherits unchanged because it re-exports the toast function directly from Sonner.

Because the wrapper defaults are better wired for a Drivn stack: five type icons are mapped out of the box, and the surface is themed from your card and border tokens so toasts match the rest of your Drivn UI immediately. The engine is identical, so the choice is about which defaults save you setup — and if you already use Drivn components, the Toast fits the same token system with no extra theme wiring.