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.
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:
- you click "post".
- wait for network.
- wait for database.
- 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
- init: it takes
initialMessagesfrom the server. - mutation: calling
addOptimisticMessageupdates the ui right away. - 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-50to 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.