Implement optimistic UI for a like button with rollback on failure

Tests state management and error recovery in async UIs. A strong answer mutates state instantly, fires the API in parallel, keeps a rollback snapshot, and reverts with a toast on failure. Red flag: waiting for the network or skipping revert logic.
What's really being asked
This question probes your ability to manage client-side state through an asynchronous boundary without degrading perceived performance. The interviewer cares about transaction-like thinking in the UI layer, specifically how you balance immediate feedback with eventual consistency and what happens when the network betrays that optimism.
The full answer
A good answer hits four things in order. First, mutate the store immediately on click: toggle the liked boolean and increment the visible count so the button flips within a single frame. Second, fire the POST request in the background without blocking further interaction. Third, preserve the pre-click state or implement a pending-transaction queue so you have a clean rollback path if the server rejects the request or times out. Fourth, handle the error path explicitly: catch the exception, revert the store to the previous liked state and count, and surface a transient toast or inline message so the user knows the action failed. Strong candidates also mention race-condition guards, such as ignoring stale responses if the user toggles the button rapidly, and optionally sending an idempotency key with the request.
The mistakes people make
Red flags include describing a pessimistic flow where the UI waits for the API to return before updating, which misses the point entirely. Another anti-pattern is mutating global state optimistically but having no rollback strategy, leaving the like stuck in the wrong state after a 500 error. Blocking the button with a loading spinner during the request also signals weak UX intuition. Finally, silently swallowing the error without reverting or notifying the user destroys trust in the interface.
What usually comes next
Interviewers often push deeper by asking how you would debounce or deduplicate rapid clicks to avoid double POSTs. They may ask what happens if the user navigates away mid-request, how you would persist the pending mutation for retry, or whether you would use a local optimistic layer versus mutating the canonical store directly. Accessibility follow-ups are common too, such as how screen readers should announce the reverted state.
A concrete example
Imagine a Pinia store holding a posts array. The user clicks like on post ID 42. The action immediately commits a mutation that sets post 42 liked to true and increments the count by one. It then caches the previous liked value and count in a closure or a separate pending map. The action fires a fetch POST to slash api slash posts slash 42 slash like. On resolve, it simply clears the pending entry. On reject, it commits a revert mutation using the cached values and dispatches a toast notification. If the user clicks again while the first request is in flight, the UI toggles optimistically but the second request is queued or deduplicated with an idempotency key to prevent a double increment on the server.
Interview question
In an optimistic like-button implementation, what should happen when the POST request fails after the UI has already toggled the liked state?
- a.Block the button with a loading spinner until the network recovers and the request succeeds
- b.Revert the liked state and count to their pre-click snapshot and show a transient failure messageCorrect
- c.Leave the new liked state visible and attempt silent background retries without notifying the user
- d.Treat the server error as non-critical and keep the optimistic update since the user intent was already registered
Why? this is the answer
Reverting the optimistic mutation and surfacing a transient toast restores consistency and maintains user trust when the network fails. Keeping the optimistic update without rollback is tempting because it honors user intent, but it leaves the UI permanently out of sync with the server after a rejected request.
Just read this? Test yourself on what you have been reading.
Read the original → crystallize.com
- #optimistic ui
- #state management
- #frontend architecture
- #error handling
- #async patterns
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles