My Next.js 16 Optimistic UI Broke Under Rapid Clicks
A Next.js 16 useOptimistic checkbox demo looked flawless until rapid clicks exposed a race condition. Fixes cover pending state, cache tags, and error.tsx.
A developer building a Next.js 16 task list with useOptimistic found the checkbox toggle worked flawlessly in every demo, until rapid, repeated clicks on the same item sent five overlapping requests to the server, leaving the database in a state that no longer matched what the UI displayed. Nothing errored visibly, which is precisely the danger with optimistic UI: looking correct and being correct are not the same thing. The fix mirrors the classic 'disable submit while in flight' pattern, just scoped per row: track which item has a pending request and disable its checkbox until that request resolves, preventing a redundant second request from ever firing.
The piece also clarifies that useOptimistic doesn't need manual rollback logic for simple toggles, since it renders a temporary layer over real state that automatically disappears once a transition settles, reverting the UI on its own if the request fails. The one exception is client-only placeholder data (like a new comment awaiting a server-assigned id), which requires manually clearing local state on failure. It further breaks down Next.js 16's three cache invalidation tools, revalidatePath, revalidateTag, and updateTag, noting revalidateTag now requires a second argument, and explains why updateTag suits data the user is actively watching while revalidateTag fits secondary views. Finally, it highlights the new unstable_retry prop introduced in Next.js 16.2 for error.tsx, which triggers a real server refetch, unlike reset, which merely re-renders existing (possibly still broken) state.