react useOptimistic: how to build snappy interfaces - By Sourav Mishra (@souravvmishra)

let's look at react's useOptimistic hook to make your next.js apps feel instant and super fast.

BySourav Mishra2 min read

today i want to talk about useOptimistic, a react hook that makes your web app feel like a native mobile app.

optimistic ui is when you show the success state before the server confirms it. think of the "like" button on twitter. it turns red instantly.


the problem with waiting

normally, a server action goes like this:

  1. you click "post".
  2. wait for network.
  3. wait for database.
  4. ui finally updates.

that waiting feels super slow. useOptimistic fixes it.


how useOptimistic helps

this hook lets you switch to a temporary state instantly while the background stuff runs.

// components/MessageList.tsx
"use client";

import { useOptimistic, useRef } from "react";
import { sendMessage } from "@/app/actions";

type Message = { id: string; text: string; sending?: boolean };

export function MessageList({ initialMessages }: { initialMessages: Message[] }) {
  const formRef = useRef<HTMLFormElement>(null);
  
  const [messages, addOptimisticMessage] = useOptimistic(
    initialMessages,
    (state, newMessage: Message) => [...state, newMessage]
  );

  async function action(formData: FormData) {
    const text = formData.get("message") as string;
    
    // show optimistic update immediately
    addOptimisticMessage({
      id: Math.random().toString(),
      text,
      sending: true,
    });

    formRef.current?.reset();
    await sendMessage(text);
  }

  return (
    <div>
      <ul>
        {messages.map((m) => (
          <li key={m.id} className={m.sending ? "opacity-50" : ""}>
            {m.text} {m.sending && "(sending...)"}
          </li>
        ))}
      </ul>

      <form action={action} ref={formRef}>
        <input name="message" className="border p-2" />
        <button type="submit">send</button>
      </form>
    </div>
  );
}

how it works behind the scenes

  1. init: it takes initialMessages from the server.
  2. mutation: calling addOptimisticMessage updates the ui right away.
  3. reconciliation: when the server finishes, the fake state is thrown away and replaced by real data.

wait, what if the server fails? okeyy, so the optimistic state just rolls back automatically. you might want to show a toast error though.


some quick tips

  • fake ids: use Math.random() for temporary keys.
  • visual cues: use things like opacity-50 to show the user it's still sending.

useOptimistic is just amazing for building high-quality next.js apps. it bridges the gap between client speed and server logic.


for ensuring valid data, check out my zod validation guide.

Share this post

Cover image for react useOptimistic: how to build snappy interfaces

You might also like

See all