Skip to content

feat(ToastProvider): migrate to 'sonner' for stacked, animated toasts#1032

Merged
sukvvon merged 2 commits into
mainfrom
refactor/toast-sonner
Jul 6, 2026
Merged

feat(ToastProvider): migrate to 'sonner' for stacked, animated toasts#1032
sukvvon merged 2 commits into
mainfrom
refactor/toast-sonner

Conversation

@sukvvon

@sukvvon sukvvon commented Jul 4, 2026

Copy link
Copy Markdown
Member

Summary

Replaces @radix-ui/react-toast with sonner as the toast backend in ToastProvider. The public useToast().notify() API is unchanged, so no consumer files were touched.

Motivation

The previous setup used Radix Toast only as a thin shell — the queue and notify() API were hand-rolled, and only Toast.Root / Toast.Viewport were borrowed. Moving to sonner (a dedicated toast library) delegates that work to a more mature implementation and improves the user-facing behavior:

  • Stacked toasts — the old Toast.Viewport laid toasts out in a flex-col with no cap, so firing several in a row filled the right side of the screen and covered page content (e.g. the partners column). sonner shows at most a few and collapses the rest into a stack (expanding on hover), so the screen is no longer buried.
  • Mobile layout — the old viewport kept the fixed w-96 (384px) width on small screens, so on a 375px viewport toasts overflowed and the text was clipped, and multiple toasts stacked down the whole screen. sonner applies a mobile layout below 600px (full-width minus offsets), so toasts fit the viewport and no longer overflow.
  • Enter/exit animations — the previous card had no data-[state] animation classes, so toasts appeared and disappeared abruptly. sonner animates them.
  • Pause-on-hover — the auto-dismiss timer pauses while the pointer is over a toast, giving users time to read.
  • Accessibilitysonner renders an aria-live="polite" region labeled Notifications with an Alt+T focus hotkey out of the box.

Preserved (unchanged)

Kept identical to the previous implementation so this is a drop-in swap:

  • useToast().notify(content, options?) signature — same (React.ReactNode, { durationMs? }) => string, so all 19 consumer files are untouched.
  • Default durationoptions?.durationMs ?? 2500 (2500ms), same as before; consumers passing a custom durationMs still work.
  • Visual style — same Tailwind classes for the toast card (rounded-md border ... dark:bg-gray-800 ...) and content (flex items-start gap-2), applied via sonner's unstyled + classNames.
  • Placement — bottom-right with a 16px offset, matching the old fixed bottom-4 right-4 viewport.
  • Swipe to dismiss — kept right-only via swipeDirections={['right']}, matching the old Radix swipeDirection="right" (sonner would otherwise also allow down-swipe at the bottom-right position).
  • Dark mode — driven by useTheme().resolvedTheme.

Changes

  • package.json: remove @radix-ui/react-toast, add sonner
  • src/components/ToastProvider.tsx:
    • Replace the Radix Toast.Provider / Toast.Root / Toast.Viewport render tree with a single <Toaster />.
    • Drop the hand-rolled toast queue (toasts state, ToastItem type, removeToast); sonner manages the queue internally.
    • notify() now calls sonner's toast() while keeping the same signature and default 2500ms duration.
    • Set position="bottom-right", offset={16}, gap={8}, and swipeDirections={['right']} to match the previous viewport placement and swipe behavior.

AS-IS

Light (Several clicked)

before_light

Dark (Several clicked)

before_dark

Mobile

image

TO-BE

Light (Several clicked)

after_light

Hover mode

after_light_hover

Dark (Several clicked)

after_dark

Hover mode

after_dark_hover

Mobile

image

Mobile (Several clicked)

image

Mobile (Several clicked and clicked specipic item)

image

Notes

  • Net change: +35 / -70 lines — queue management is now delegated to sonner.

Summary by CodeRabbit

  • New Features
    • Upgraded in-app toast notifications to the newer toast experience with improved theme awareness and consistent bottom-right placement.
    • Toasts now use a duration setting (defaulting to 2500ms) and return a notification ID from the notify action.
  • Bug Fixes
    • Simplified toast handling to reduce issues with overlapping notifications and improve overall reliability.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Replaced the Radix toast dependency with sonner, and updated ToastProvider to use sonner’s toast API and Toaster component instead of Radix-based local toast state and rendering.

Changes

Toast library migration

Layer / File(s) Summary
Dependency swap
package.json
Removes @radix-ui/react-toast and adds sonner at ^2.0.7.
ToastProvider reimplementation
src/components/ToastProvider.tsx
Replaces Radix imports/types with sonner imports, reworks notify to call sonner’s toast() and return a stringified id, and renders a themed Toaster instead of manually mapped Toast.Root elements with local state.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Consumer
  participant ToastProvider
  participant SonnerToast
  participant Toaster

  Consumer->>ToastProvider: notify(content, options)
  ToastProvider->>SonnerToast: toast(content, { duration })
  SonnerToast-->>ToastProvider: toast id
  ToastProvider-->>Consumer: id.toString()
  SonnerToast->>Toaster: render toast
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating ToastProvider to sonner for stacked, animated toasts.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/toast-sonner

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 4, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
tanstack-com dc23828 Commit Preview URL

Branch Preview URL
Jul 04 2026, 07:34 PM

@sukvvon sukvvon self-assigned this Jul 4, 2026
@sukvvon sukvvon marked this pull request as ready for review July 4, 2026 17:28
@sukvvon sukvvon requested a review from a team July 4, 2026 17:30
@sukvvon sukvvon changed the title feat(ToastProvider): migrate to 'sonner' for animated, accessible toasts feat(ToastProvider): migrate to 'sonner' for stacked, animated toasts Jul 6, 2026
@sukvvon sukvvon merged commit 0b90bdb into main Jul 6, 2026
7 checks passed
@sukvvon sukvvon deleted the refactor/toast-sonner branch July 6, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants