• Home
  • About
  • Agent Skills
  • Projects
  • Blog
  • Contact
Resume
Naser Rasouli

Author

Naser Rasouli

Front-End developer - sharing lessons learned, notes, and write-ups from real projects.

GitHubLinkedIn

Last posts

Debugging INP in React Apps: From Field Data to Profiler
2026-09-10•1 min read

Debugging INP in React Apps: From Field Data to Profiler

A practical workflow for tracing slow React interactions from field data and long tasks to event handlers, React renders, and main-thread work.

JavaScript SEO for React: What Google Actually Sees
2026-09-10•1 min read

JavaScript SEO for React: What Google Actually Sees

A practical React SEO guide to crawling, rendering, indexing, SSR and prerendering, internal links, metadata, and client-rendered content.

Type-Safe React Forms with React Hook Form and Zod
2026-09-07•1 min read

Type-Safe React Forms with React Hook Form and Zod

A schema-first approach to React forms with React Hook Form and Zod, covering inferred types, field arrays, API errors, conditional fields, and client/server validation boundaries.

Optimistic UI in React Without the Common Bugs

Optimistic UI in React Without the Common Bugs

2026-09-11
reactoptimistic-uiuseOptimistictransitionsux

Optimistic UI in React Without the Common Bugs

Optimistic UI renders the outcome a user is likely to get before the server has finished the mutation. It can make likes, status changes, comments, and small edits feel immediate. The difficult part is keeping that temporary projection separate from authoritative server data.

React's useOptimistic is useful for that temporary projection. The server should still remain the source of truth.

Choose optimistic operations deliberately

Good candidates have a predictable outcome, a low failure rate, and a recovery path the user can understand. A financial transfer, complex checkout, or operation whose result depends heavily on server-side policy usually deserves a more conservative loading state.

Project from real state instead of cloning it

import { startTransition, useOptimistic } from "react"

type Comment = { id: string; body: string; pending?: boolean }

function Comments({ comments }: { comments: Comment[] }) {
  const [optimisticComments, addOptimisticComment] = useOptimistic(
    comments,
    (current, draft: Comment) => [...current, draft],
  )

  async function submit(body: string) {
    const draft = { id: crypto.randomUUID(), body, pending: true }
    startTransition(async () => {
      addOptimisticComment(draft)
      await createComment({ body })
    })
  }

  return optimisticComments.map((comment) => (
    <article key={comment.id} aria-busy={comment.pending}>{comment.body}</article>
  ))
}

The temporary item exists to make the interaction responsive. Once authoritative data arrives, that data should drive the rendered result.

Treat temporary identity as temporary

If the server assigns IDs, do not pretend a client-generated ID is final. A common duplication bug happens when an optimistic item is kept locally and the successful server response is appended as another item.

Design failure before shipping success

Recovery may mean restoring the previous value, keeping an editable draft, or offering retry. Destructive and multi-step operations need more care than a reversible toggle.

Decide what concurrent actions mean

Responses do not have to arrive in click order. Define whether actions are independent, whether the latest intent wins, whether the server exposes an authoritative version, or whether duplicate actions should be temporarily prevented.

Transitions do not replace mutation policy

A Transition helps React schedule non-urgent work. It does not decide retry rules, validate server responses, or define rollback behavior.

User intent
   ↓
Optimistic projection
   ↓
Server mutation
   ↓
Success → authoritative state
Failure → feedback / recovery

Avoid two optimistic owners

If TanStack Query or another server-state library owns the resource, avoid building a second independent cache protocol around the same data. Use one clear owner for mutation lifecycle and cache reconciliation.

Production checklist

Verify that server data remains authoritative, pending UI is distinguishable, failures have a recovery path, successful responses cannot create duplicates, repeated actions have defined semantics, and the feature does not maintain an unnecessary permanent mirror of server state.

Optimistic UI works best when it hides latency without hiding uncertainty. If reconciliation becomes more complicated than the interaction itself, reconsider whether the operation should be optimistic at all.