Following #1843, I tried to actually delete an app's hand-rolled toast (193 lines) and adopt toast. It cannot be done without losing behaviour. Writing up the gap concretely, since #1843 counted call sites but did not check whether the primitive covers them.
The app has 16 toast call sites across 5 views. Here is what toast does not carry.
| Needed | toast today | Used at |
|---|---|---|
| Title distinct from message | none — msgSpan.textContent = message and nothing else | ~all 16 sites ('Published', 'Could not save rule', 'Listening scan complete') |
| Replace by semantic id | dismiss(id) takes the numeric return of addToast | 8 sites: id: 'publish', 'listening-save', 'timeline-load', 'listening-scan' |
| Action buttons / link | none | the publish-success toast offers "Open on Bluesky" + "Copy link" |
| Dedupe | none | suppresses an identical toast within 2–6s; without it a retry loop stacks the same error |
| Loading state | none | an in-progress publish shows a spinner and duration: 0, replaced on completion |
The id one is the sharpest. The app publishes a post, shows a persistent "Publishing…" toast under id: 'publish', and on completion calls dismiss('publish') from a different function. With toast, the numeric id has to be threaded between those two call sites by hand — which is the state-management the primitive is supposed to remove.
title is the most common. Every site passes one, and collapsing title and message into a single string loses the visual hierarchy that makes a toast scannable.
Why this matters beyond one app
These are the same five things any toast library ships, so this is the shape of hand-rolled toast generally, not one app's taste. As it stands, an app that needs any of them keeps its own implementation, and toast is only reachable for the simplest notifications — which is a fair scope, but then #1843's "the primitive is delivered and unused" reads as an adoption problem when it is partly a capability one.
Suggested
title and a string id (replace-in-place) are the two that unblock the most, and neither changes the existing signature:
toast.success('Post published.', { title: 'Published', id: 'publish' })
toast.dismiss('publish')Actions and dedupe are bigger; happy to file separately if useful. Also worth stating in the docs which of these toast is not meant to cover, so the next person hits the boundary at read time rather than mid-migration.
Related: #1912 (toast ignores the app theme) is a blocker for the same migration — a toast that renders dark over a light app is not shippable regardless of the API.