Next.js cookbook · Intermediate
Optimistic UI with Server Actions
Make liking a post or adding a comment feel instant while the Server Action runs.
Approach
Combine a Server Action with React's useOptimistic: apply the optimistic change immediately in a transition, call the action, and let revalidation bring back the real data. If the action throws, React reverts to the server state automatically.
Solution
// app/posts/[id]/actions.ts
"use server";
import { revalidatePath } from "next/cache";
export async function addComment(postId: string, text: string) {
const session = await verifySession();
if (text.trim().length === 0 || text.length > 1000) throw new Error("Invalid comment");
await db.comment.create({ data: { postId, authorId: session.userId, text } });
revalidatePath(`/posts/${postId}`);
}
// app/posts/[id]/Comments.tsx
"use client";
import { useOptimistic, useRef, startTransition } from "react";
import { addComment } from "./actions";
export function Comments({ postId, comments }: { postId: string; comments: Comment[] }) {
const formRef = useRef<HTMLFormElement>(null);
const [optimistic, addOptimistic] = useOptimistic(comments, (state, text: string) => [
...state, { id: `temp-${Date.now()}`, text, author: "You", pending: true },
]);
async function submit(formData: FormData) {
const text = String(formData.get("text") ?? "");
formRef.current?.reset();
startTransition(() => addOptimistic(text));
await addComment(postId, text);
}
return (
<section>
<ul>{optimistic.map((c) => <li key={c.id} style={{ opacity: c.pending ? 0.6 : 1 }}>{c.author}: {c.text}</li>)}</ul>
<form ref={formRef} action={submit}><input name="text" required maxLength={1000} /><button>Post</button></form>
</section>
);
}
Pitfalls
- Optimistic updates for actions likely to fail validation.
- No visual hint that an item is pending.
- Forgetting revalidation, so the optimistic item disappears or duplicates.
Variations
- TanStack Query mutations with optimistic cache updates for client-fetched data.
Further reading: react.dev: useOptimistic
Reference: https://react.dev/reference/react/useOptimistic
More Next.js recipes
- Add a Content Security Policy with nonces in Next.js
- Add dark mode to Next.js without a flash
- Add SEO metadata, Open Graph and sitemaps
- Avoid data-fetching waterfalls in Server Components
- Build a validated JSON API with Route Handlers
- Create encrypted session cookies in Next.js
- Handle errors and 404s in the App Router
- Handle forms with Server Actions and validation