Prompt of the Day: Build an Optimistic UI Update Pattern
Part 22 of 30 — Daily Prompts Series
Optimistic UI updates mean your interface responds the moment a user acts — before the server says a word. Click like, the count goes up. Check a todo, it checks. If the server later says no, you roll back. The result is an app that feels native-fast rather than network-slow.
This pattern has gone from clever trick to table stakes. React 19 shipped useOptimistic as a first-class hook because the demand was overwhelming — as Syncfusion's February 2026 deep-dive put it: "Modern users expect instant feedback when interacting with web applications. Waiting for server responses before updating the UI leads to poor user experience." And freeCodeCamp's December 2025 breakdown makes the stakes plain: rollback logic isn't optional — omitting it causes data integrity failures that erode exactly the trust you were trying to build.
The Prompt
Implement an optimistic UI update pattern for [DESCRIBE YOUR ACTION — e.g. "liking a post", "checking a todo item", "adding a comment"] in my React + Next.js app using the App Router.
Tech stack: React 19, Next.js 15, TypeScript, Tailwind CSS, [React Query | SWR | Next.js Server Actions — pick one].
Requirements:
1. Use `useOptimistic` (for Server Actions) OR `useMutation` with `onMutate` / `onError` / `onSettled` (for React Query) to apply the UI change immediately when the user triggers the action.
2. Define all relevant TypeScript types — the entity type, the optimistic state shape, and the action/mutation function signature.
3. On server error, roll back to the previous confirmed state and display a user-facing error message (toast or inline).
4. Show a subtle loading indicator (opacity reduction, spinner, or disabled state) while the request is in flight, so power users know something is happening.
5. Do NOT block the user — the interaction must feel instant even on a slow connection.
6. Add a comment above each key section explaining the optimistic pattern step: (a) capture snapshot, (b) apply optimistic update, (c) run async operation, (d) rollback on error, (e) confirm on success.
Output: a complete, self-contained client component with the server action or API call stubbed out. Include TypeScript types inline.
Why It Works
This prompt is effective because it removes ambiguity at every decision point where AI coding tools tend to go wrong.
Specificity of the action. The placeholder [DESCRIBE YOUR ACTION] forces you to think about what's being optimized before the AI writes a single line. A vague action produces vague code. A specific action — "toggling the archived state of an invoice" — produces code you can actually ship.
Explicit tech stack choice. useOptimistic and React Query's onMutate pattern are architecturally different. Mixing them produces broken code. Specifying one up front prevents the AI from guessing.
Rollback as a hard requirement. Without explicitly requesting rollback, most AI-generated optimistic updates are actually just fake fast — they update the UI and never reconcile on failure. The prompt treats rollback as non-negotiable.
The comment requirement is the secret weapon. Asking the AI to annotate each pattern step forces it to produce structured, reviewable code rather than a blob. When you read those comments, you immediately know if the model understood the pattern or just mimicked the syntax.
The Anti-Prompt
Make my like button update instantly
Here's why this fails:
- No stack context. The AI might reach for
useStatewith asetTimeoutfake delay. That's not optimistic UI — that's theater. - No rollback. The AI will almost certainly skip it. You'll ship code that permanently shows the wrong count when a network request fails.
- No TypeScript types. You'll get
anyeverywhere, and you won't catch type mismatches until runtime. - No loading state guidance. The button will either feel broken (no visual feedback) or double-submit (not disabled during the request).
- No architecture constraint. If you're using Server Actions today and React Query tomorrow, you need different implementations. An ambiguous prompt gets you whichever one the AI saw most recently in its training data.
The anti-prompt produces code that looks right in a sandbox demo and breaks in production under real network conditions.
Variations
Variation 1 — Next.js Server Actions + useOptimistic
Implement optimistic UI for [ACTION] using Next.js 15 Server Actions and React 19's `useOptimistic` hook. Wrap the server action call in `startTransition`. The optimistic state should revert automatically when the transition completes and the server-rendered state is revalidated via `revalidatePath`. Include a `pending` flag on the optimistic item so I can style it with reduced opacity while in flight.
Best for: full RSC (React Server Component) architectures where you want the server to be the source of truth after every mutation.
Variation 2 — React Query useMutation
Implement optimistic UI for [ACTION] using TanStack Query v5's `useMutation`. In `onMutate`, cancel in-flight queries, snapshot the previous data, and apply the optimistic update to the query cache. In `onError`, restore the snapshot. In `onSettled`, invalidate the query to force a refetch. Show a loading state using the `isPending` flag from the mutation.
Best for: apps already using React Query for data fetching — you get cache management, retries, and devtools for free.
Variation 3 — Plain Fetch with useState Rollback
Implement optimistic UI for [ACTION] without any external libraries — just React's `useState` and a standard `fetch` call. Capture the previous state before updating. Run the fetch in a try/catch block. On catch, restore the previous state and call `setError` with a user-friendly message. Disable the trigger element while `isLoading` is true.
Best for: lightweight projects, micro-frontends, or any context where you can't add dependencies.
When You Actually Need This
Optimistic UI earns its complexity in high-frequency, low-stakes interactions: likes, bookmarks, votes, todo toggles, cart quantity changes, comment submissions. These are actions users repeat dozens of times per session. A 300ms delay on each one is a death by a thousand cuts — users feel it even when they can't name it.
Skip the pattern for high-stakes, low-frequency actions: payments, deletions, account changes. There, the loading spinner is a feature — it signals that something important is happening. Optimism is appropriate when the cost of being wrong is a quick rollback. It's inappropriate when being wrong means a user thinks a payment went through when it didn't.
Ask The Guild
What's the trickiest optimistic update you've implemented? Did you hit race conditions when a user triggered the same action twice before the first server response came back? Share your rollback strategy — or your cautionary tale — in the thread below.