HarshPatel

Ahmedabad, Gujarat
Back to Blog
ReactReact19HooksuseOptimisticFrontend

React 19 useOptimistic Hook: Build Snappy UIs That Update Before the Server Responds

Harsh PatelMay 4, 20263 min read8 views
React 19 useOptimistic Hook: Build Snappy UIs That Update Before the Server Responds

Introduction

You know that satisfying feeling when you like a tweet and the heart turns red instantly? That's optimistic UI — the app shows the result before the server confirms it. If the server fails, it rolls back.

Before React 19, implementing this pattern required a lot of boilerplate. Now, the useOptimistic hook makes it trivial. Here's how it works.

What is useOptimistic?

useOptimistic takes your current state and returns an "optimistic" version of it — one that updates immediately when you trigger an action, and reverts to the real state once the async operation settles.

const [optimisticState, addOptimistic] = useOptimistic(state, updateFn);

Example 1: Like button

"use client";

import { useOptimistic, useState } from "react";
import { toggleLike } from "@/actions/likes";

export function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: number }) {
  const [likes, setLikes] = useState(initialLikes);
  const [liked, setLiked] = useState(false);

  const [optimisticLikes, addOptimisticLike] = useOptimistic(
    likes,
    (currentLikes, newLiked: boolean) => currentLikes + (newLiked ? 1 : -1)
  );

  async function handleLike() {
    const newLiked = !liked;
    addOptimisticLike(newLiked); // Instant UI update
    setLiked(newLiked);

    try {
      const updated = await toggleLike(postId);
      setLikes(updated.likes);
    } catch {
      setLiked(!newLiked); // Rollback on error
    }
  }

  return (
    
  );
}

Example 2: Optimistic todo list

"use client";

import { useOptimistic, useState } from "react";
import { addTodo } from "@/actions/todos";

export function TodoList({ initialTodos }) {
  const [todos, setTodos] = useState(initialTodos);

  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (currentTodos, newTodo) => [...currentTodos, { ...newTodo, pending: true }]
  );

  async function handleSubmit(formData: FormData) {
    const text = formData.get("text") as string;
    const tempTodo = { id: Date.now(), text, completed: false };

    addOptimisticTodo(tempTodo); // Show immediately

    const savedTodo = await addTodo(text);
    setTodos((prev) => [...prev, savedTodo]);
  }

  return (
    
    {optimisticTodos.map((todo) => (
  • {todo.text}
  • ))}
); }

Notice the pending: true flag — you can use this to show a loading style on items that haven't been confirmed yet.

When to use useOptimistic

  • Like / upvote / follow buttons
  • Adding items to a list
  • Marking items as complete
  • Any mutation where the success rate is high and latency matters

Don't use it for destructive actions (deleting accounts, financial transactions) where showing a false state could be harmful.

Conclusion

useOptimistic is one of my favourite additions in React 19. It codifies a pattern that previously required custom state management into a clean, two-line hook. Combined with Server Actions, it makes building responsive, server-synced UIs dramatically simpler.

All Posts
ReactReact19HooksuseOptimisticFrontend