We replaced our toast library with 230 lines. The strings were the hard part.
Sonner is a good toast library. Our problem was not the library. It was what happened when someone tapped Save three times and received three identical confirmations climbing up the screen.
We replaced it with about 230 lines of React and Motion. The first version took a day. The wording and state rules took much longer.
One operation should own one notification
When the same action happens again, the system now updates the existing toast. It increments a count, gives the capsule a small pulse and restarts the dismissal timer. The user sees that every tap registered without having to read the same sentence three times.
The same rule made progress states simpler. A toast that begins as “Saving from clipboard…” keeps its identity and becomes “Saved”. It does not disappear so that another message can take its place. One object changes because one operation changed.
The words had to carry the state
Most success messages became short outcomes: “Saved”, “Copied”, “Removed”. A description appears only when it adds a useful consequence, such as where an item went or what changed next.
Delivery messages are different. “Your look is ready” can justify the possessive because the person waited for something made for them. An error needs an action when one exists. “Could not save this item” is only useful if the next line says whether to try again, wait or change something.
Motion shows continuity
The pulse on a repeated save is not decoration. It shows that the existing object changed. The loading state cannot be dismissed because the operation is still running. Once the operation ends, the toast becomes draggable and can be flicked back towards the top edge it entered from.
We kept a pill for short messages and let it expand when a description was necessary. Variants still distinguish success, error and branded moments, but colour never has to explain the message on its own.
Why the custom system was worth it
The finished component is small because it does not try to solve every notification problem. It solves the ones our product actually had: repeated actions, operations that change state and messages whose wording needs to stay consistent.
The animation made the system feel coherent. The copy made it understandable. That turned out to be the harder part.