Skip to content

fix: clean up dismissed toasts from internal array to prevent memory leak - #772

Open
q121212 wants to merge 1 commit into
emilkowalski:mainfrom
q121212:fix/toast-memory-leak
Open

fix: clean up dismissed toasts from internal array to prevent memory leak#772
q121212 wants to merge 1 commit into
emilkowalski:mainfrom
q121212:fix/toast-memory-leak

Conversation

@q121212

@q121212 q121212 commented Jul 11, 2026

Copy link
Copy Markdown

Fixes #729 — memory leak in ToastState where dismissed toasts are never cleaned up.

Problem: The Observer class maintains a this.toasts array that accumulates every toast ever created. The dismiss method only adds IDs to this.dismissedToasts (a Set) but never removes the actual toast objects from this.toasts. Since each toast can contain JSX (React elements), this causes unbounded memory growth in long-running SPAs.

Fix:

  1. Remove dismissed toasts from this.toasts after exit animation (400ms delay) so the array doesn't grow forever
  2. Add clearHistory() method for manual cleanup, exposed via toast.clearHistory()
  • Type check passes
  • Build succeeds
  • No breaking API changes

…leak

Dismissed toasts retained React JSX element references forever in
ToastState.toasts because the dismiss method only tracked them in
dismissedToasts but never removed them from the internal array.

This caused unbounded memory growth in long-running SPAs, especially
with custom JSX toasts that hold component instances, hooks, and
event handlers.

Fix:
- Remove dismissed toasts from this.toasts after exit animation (400ms)
- Add clearHistory() method for manual cleanup
- Expose clearHistory in the public toast API

Fixes emilkowalski#729
@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sonner Error Error Jul 11, 2026 10:12am

Request Review

@MILLERMARRU MILLERMARRU left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked state.ts and dismiss() genuinely never removes anything from this.toasts, it only marks the id in dismissedToasts and notifies subscribers. The only place the array shrinks is trimHistory, which only kicks in past 100 toasts, so this is a real unbounded-growth leak for any app that creates and dismisses toasts steadily below that count, holding onto full toast objects (including JSX for custom toasts) indefinitely. Using a 400ms timeout to let the exit animation finish before removing from the array is reasonable, though it's coupled to the animation duration as a magic number rather than reading it from the actual CSS/config, if that duration ever changes elsewhere in the codebase this cleanup could fire slightly early or late without anything failing loudly. Worth checking whether getHistory() callers expect a dismissed-but-still-animating toast to still be present in the returned array during that 400ms window, since this changes that visible behavior slightly.

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.

Memory leak in ToastState - dismissed toasts never cleaned up

2 participants