Tommy Jepsen
A small right-pointing arrow on a dark background

Optimistic UI Is a Promise, Not a Speed Trick

2026-08-28 — by Tommy Jepsen

When you hit the like button on a post, the heart fills instantly. The request to the server hasn't finished. It might not even have left your phone yet. The UI just decided the answer in advance and drew it.

That's optimistic UI. You assume the write will succeed, show the result immediately, and quietly reconcile later when the server answers. It's one of the cheapest ways to make a product feel fast, and it's also the pattern I see implemented halfway most often.

It works because most requests succeed

The bet is boring math. A like, a checkbox, a drag into another column: these succeed almost every time. So instead of paying 300ms of spinner on every interaction to be honest about the 1% that fail, you pay nothing on the 99% and handle the rest as an exception.

The win isn't really the milliseconds. It's that the interface stops asking the user to wait for permission. You drag a card, it's there. You rename a file, it's renamed. The mental model becomes "I changed this thing" instead of "I asked the system to change this thing". That second model is what makes slow software feel like paperwork.

The rollback is the actual design work

Here's where it usually falls apart. The happy path is easy, and it's the part that gets built. Then the request fails and the UI has already told the user something that isn't true.

The lazy fix is to just snap the state back. The heart un-fills, the card slides back to the old column, and nothing tells the user why. From their side, the app spontaneously undid their work. That's worse than a spinner, because now they don't trust the fast version either.

What the failure needs is: put the state back, say what failed in plain words, and keep the user's input recoverable. Linear does this well on issue updates. If a change doesn't land you get a small toast naming the thing that failed, and the issue returns to its old state rather than sitting in a broken half-applied one. Compare that to a comment box that clears itself on submit and then loses the comment when the request 500s. The text was the expensive part, and it's gone.

So the rule I use: never optimistically clear an input. Optimistically show the result, but hold the user's typing until the server confirms. Rolling back a heart icon costs nothing. Rolling back four paragraphs someone wrote costs them the whole thing.

When I skip it

Optimistic UI is a promise, and there are places I don't want to promise.

Anything with money. "Payment sent" should mean the payment was sent. Anything irreversible, like a delete that isn't soft. Anything where the server does real validation the client can't replicate, so the client's guess about the outcome is genuinely a guess. If your optimistic state is going to be wrong a noticeable share of the time, you're not making the product feel fast, you're making it feel flaky.

There's also the case where the write depends on other people. A seat booking, a username claim, inventory on the last item in stock. The client has no idea if it won the race. Showing "yours" and then taking it away is a small betrayal, and users remember those better than they remember loading times.

Where I've landed

For most CRUD in a product I build, optimistic by default. Toggles, reordering, renaming, marking things done. It's a normal part of what a mutation looks like now, and libraries like TanStack Query or SWR give you the rollback hook for free, so there's no real excuse for the snap-back-and-say-nothing version.

The part I find useful as a designer is that the pattern forces you to name your failure states early. You can't build optimistic UI without deciding what the app says when it's wrong. A pessimistic UI lets you avoid that conversation, because the spinner absorbs it and the error appears in the same place the user was already looking.

Which is maybe the honest summary. Optimistic UI isn't a speed trick. It's you taking a position on how confident your backend is, and then designing what happens when that confidence was misplaced. Skip the second half and you've just built a UI that lies.

Tommy Jepsen - design engineer in Copenhagen

Let's connect on LinkedIn ✌️

Dropping random thoughts, AI experiments, product design insights and more.