← Back
Edit Post
Title
Description
Instant feedback with useOptimistic, derived pending state, data-pending for parent styling.
Content
Markdown supported
# useOptimistic The best in-between state is **no perceived delay at all**. When a user toggles a setting, bookmarks an item, or archives a post, the UI should update instantly—even while the server action runs in the background. `useOptimistic` creates a local state that updates immediately, then syncs with the actual server state when the action completes. If the action fails, React automatically reverts to the true state. ## The Archive Button Pattern Here's an example using `useOptimistic` for instant archive toggle: ```tsx 'use client'; import { useOptimistic, startTransition } from 'react'; export function ArchiveButton({ slug, archived }) { const [optimisticArchived, setOptimisticArchived] = useOptimistic(archived); const isPending = optimisticArchived !== archived; return ( <form action={async () => { startTransition(() => setOptimisticArchived(!optimisticArchived)); await toggleArchivePost(slug, !archived); }}> <button type="submit" disabled={isPending}> {optimisticArchived ? 'Unarchive' : 'Archive'} </button> </form> ); } ``` Click archive → icon switches instantly → server updates in background → states sync. ## Deriving Pending State Notice there's no separate `isPending` from `useTransition`. Instead, we derive it: ```tsx const isPending = optimisticArchived !== archived; ``` While the action runs, the optimistic value differs from the prop. When the action completes and the parent re-renders with new data, they match again and `isPending` becomes false. This is elegant—the pending state emerges from the data itself. ## Parent Styling with data-pending The `data-pending` attribute enables something powerful: Server Components can style based on Client Component state. ```tsx // In the Client Component - add data attribute based on pending <form data-pending={isPending || undefined}> // In the Server Component (PostList) <Card className="has-data-pending:animate-pulse has-data-pending:bg-muted/70"> <ArchiveButton slug={post.slug} archived={post.archived} /> </Card> ``` Tailwind's `has-[[data-pending]]:` variant uses CSS `:has([data-pending])`—the Card pulses during the action without becoming a Client Component itself. ## When to Use Optimistic Updates Best for high success rate, reversible actions: - Toggles (archive, bookmark, like) - Status changes - Settings updates Avoid for actions that need validation feedback or have meaningful failure modes—use `useTransition` instead so you can handle errors before updating UI.
Published
Save Changes
Cancel