← Back
Edit Post
Title
Description
isPending for destructive actions, comparison with useOptimistic, keeping content visible.
Content
Markdown supported
# useTransition For destructive or high-stakes actions, users need immediate feedback that something is happening and protection from accidental double-clicks. `useTransition` provides a pending state that wraps the entire async operation—including any navigation that follows. Unlike `useOptimistic` which updates UI immediately and hopes for success, `useTransition` waits for confirmation before updating. This is the right choice when you need to handle errors before showing results. ## The Delete Button Pattern Deleting a post should be deliberate—disable the button, show progress, then navigate away: ```tsx 'use client'; import { useTransition } from 'react'; import { useRouter } from 'next/navigation'; export function DeletePostButton({ slug }) { const [isPending, startTransition] = useTransition(); const router = useRouter(); function deleteAction() { startTransition(async () => { const result = await deletePost(slug); if (result.success) { router.push('/dashboard'); } }); } return ( <Button variant="destructive" onClick={deleteAction} disabled={isPending}> {isPending ? 'Deleting...' : 'Delete Post'} </Button> ); } ``` `isPending` becomes true immediately when `startTransition` is called. It stays true through the entire async operation **and** any navigation triggered inside—users can't click again until everything completes. ## When to Use useTransition vs useOptimistic | Hook | UI Update | Best For | |------|-----------|----------| | `useOptimistic` | Instant, before action completes | Toggles, likes, reversible actions | | `useTransition` | After action completes | Destructive actions, error handling needed | ## Transitions Keep Content Visible During navigation wrapped in `startTransition`, React keeps the current UI visible instead of showing Suspense fallbacks. The pending state lets you show a loading indicator while preserving context. ## Nested Transitions If you need to update state after an await inside a transition, wrap it in another transition: ```tsx startTransition(async () => { await deletePost(slug); startTransition(() => { router.push('/dashboard'); }); }); ``` This ensures React batches both the action completion and the navigation correctly.
Published
Save Changes
Cancel