How to Migrate from Deprecated shadcn/ui Toast Components to Sonner

To migrate from the deprecated toast and toaster components to Sonner, install the sonner package, replace your Toaster import with the wrapper from @/registry/new-york/ui/sonner, and update all toast function imports from the local registry path to import { toast } from "sonner".

The shadcn/ui library has officially deprecated the legacy Radix-based toast system in favor of Sonner, a modern toast library with built-in theme support and a richer API. This migration guide provides the exact code changes required, referencing the source files from the shadcn/ui repository where the deprecation is declared and the new implementation resides.

Understanding the Deprecation

The deprecation is explicitly defined in packages/shadcn/src/registry/constants.ts, where the DEPRECATED_COMPONENTS array marks both toast and toaster as replaced by sonner【61†L61-L73】. While the old components remain available in the deprecated/www/registry/new-york/ui/ directory for backwards compatibility, the new implementation lives in deprecated/www/registry/new-york/ui/sonner.tsx【61†L1-L9】.

Sonner offers significant improvements over the legacy system: automatic integration with next-themes for light/dark mode switching, built-in helper methods like toast.success() and toast.promise(), and a lighter runtime footprint compared to the Radix-based implementation.

Step 1: Install the Sonner Dependency

Ensure the sonner package is installed in your project. The shadcn/ui monorepo currently locks this dependency at version 2.0.7 in pnpm-lock.yaml【61†L7075】.

npm i sonner

Leave your existing next-themes installation as-is; the Sonner wrapper depends on it for automatic theme detection.

Step 2: Replace the Toaster Component

Update your root layout or main application file to import the Toaster from the new Sonner wrapper instead of the deprecated toaster component.

Before (deprecated):

import { Toaster } from "@/registry/new-york/ui/toaster";

export function App() {
  return (
    <>
      <Toaster />
      {/* ...rest of app */}
    </>
  );
}

After (Sonner):

import { Toaster } from "@/registry/new-york/ui/sonner";

export function App() {
  return (
    <>
      <Toaster />
      {/* ...rest of app */}
    </>
  );
}

The new Toaster component in sonner.tsx forwards all props to the underlying Sonner library while injecting the current theme via useTheme() from next-themes【61†L1-L9】.

Step 3: Migrate the Toast Function

Replace all imports of the toast function from the local registry path with the direct import from the sonner package. This is the most critical change for functionality.

Before (deprecated):

import { toast } from "@/registry/new-york/ui/toast";

function handleSave() {
  toast("Saved successfully!", {
    description: "Your changes have been persisted.",
    action: {
      label: "Undo",
      onClick: () => console.log("undo"),
    },
  });
}

After (Sonner):

import { toast } from "sonner";

function handleSave() {
  toast("Saved successfully!", {
    description: "Your changes have been persisted.",
    action: {
      label: "Undo",
      onClick: () => console.log("undo"),
    },
  });
}

Step 4: Utilize Sonner's Enhanced API

Sonner provides built-in helper methods that eliminate the need for manual variant management. These methods are not available in the deprecated shadcn implementation.

Success and error variants:

import { toast } from "sonner";

// Automatic success styling
toast.success("Item added.", {
  description: "Your new item is now in the list.",
});

// Automatic error styling
toast.error("Failed to add item", {
  description: "Please try again later.",
});

Promise-based toasts:

// Automatic loading, success, and error states
toast.promise(
  fetch("/api/save"),
  {
    loading: "Saving…",
    success: "Saved!",
    error: "Failed to save.",
  }
);

Step 5: Remove Legacy UI Components

Delete all imports and usage of the legacy toast primitives from your codebase. Remove references to ToastProvider, ToastViewport, Toast, ToastTitle, ToastDescription, ToastAction, and ToastClose from the old toast.tsx file. Sonner handles the viewport, positioning, and action buttons internally, significantly simplifying your component tree.

If you previously customized toast icons, pass them via the icons prop to the new Toaster component:

import { Toaster } from "@/registry/new-york/ui/sonner";
import { Check, X, Info } from "lucide-react";

export function App() {
  return (
    <Toaster
      icons={{
        success: <Check className="size-4" />,
        error: <X className="size-4" />,
        info: <Info className="size-4" />,
      }}
    />
  );
}

Step 6: Verify Theme Compatibility

Ensure your application root is wrapped with the next-themes provider. The Sonner wrapper in deprecated/www/registry/new-york/ui/sonner.tsx calls useTheme() to synchronize the toast appearance with your application's current theme【61†L1-L9】. Without this provider, toasts will not respond to theme changes.

Summary

  • The deprecation is declared in packages/shadcn/src/registry/constants.ts, which maps toast and toaster to sonner replacements.
  • Installation requires the sonner package (v2.0.7) as your only new runtime dependency.
  • Component migration involves changing the Toaster import from @/registry/.../ui/toaster to @/registry/.../ui/sonner.
  • Function migration requires updating import { toast } from "..." to import { toast } from "sonner".
  • API enhancements include toast.success(), toast.error(), and toast.promise() for streamlined UX patterns.
  • Cleanup involves removing all legacy ToastProvider, ToastViewport, and related primitive components.

Frequently Asked Questions

Is the old toast component removed completely?

No, the legacy components remain in the deprecated/www/registry/new-york/ui/ directory, specifically in toast.tsx【61†L1-L9】, for backwards compatibility. However, they are marked for removal in future releases and receive no new features or bug fixes. Migrating to Sonner ensures you stay on the supported path.

Do I need to install next-themes separately?

If you already use next-themes in your project, no additional installation is required. The Sonner wrapper imports useTheme from next-themes to handle automatic light/dark mode switching. If you are not using next-themes, the wrapper will still function but will default to the light theme unless you manually configure the theme prop on the Toaster component.

How do I customize Sonner toast icons and styling?

Pass custom icons via the icons prop on the Toaster component, or override default classes using the toastOptions.classNames prop. The wrapper already provides default styling with className="toaster group", but you can extend this via the className prop on Toaster or by targeting Sonner's CSS classes in your global stylesheet.

Will my existing toast trigger logic break during migration?

Basic toast calls using the pattern toast("Message", { description: "...", action: {...} }) remain functionally identical. The only breaking change is the import path. However, if you relied on the old Toast component's variant prop for styling, you must replace those calls with toast.success(), toast.error(), or toast.info() to achieve the same visual results.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →