next-safe-action React Hooks
Import
Which Hook
Pick by action kind first: .action() pairs with useAction / useOptimisticAction, .stateAction() pairs with useStateAction / useOptimisticStateAction.
useAction — Quick Start
useOptimisticAction — Quick Start
useOptimisticStateAction — Quick Start
For overlapping writes that must accumulate (reorder an item, then reorder it again before the first save lands). Dispatches are queued: each waits for the previous to settle, and the server receives the last confirmed state as prevResult.
The action must be a .stateAction(), and must revalidate the state the page renders:
See useOptimisticStateAction in depth for the decision rules, the pending-changes-list shape, and the gotchas.
useStateAction — Quick Start
The server-side action must use .stateAction() (not .action()). <form action={formAction}> submits raw FormData, so the input schema must parse FormData (e.g. with zod-form-data):
Return Value
All hooks (useAction, useOptimisticAction, useStateAction, useOptimisticStateAction) return:
useOptimisticAction additionally returns:
| optimisticState | State | The optimistically-updated state |
useStateAction additionally returns:
| formAction | (input) => void | Dispatcher for <form action={formAction}> pattern |
useOptimisticStateAction returns everything useStateAction returns, plus:
| optimisticState | State | Confirmed state folded with every in-flight change (always defined) |
The hook return is itself a discriminated union keyed on status and every has* / is* shorthand (each typed as literal true / false per branch). Narrowing any discriminant narrows result — e.g. inside if (hasSucceeded), result.data is Data (not Data | undefined). See Type narrowing via hook status.
initResult Option
All hooks accept initResult to seed the hook with a preloaded result (e.g. data fetched on the server): in the opts object for useAction/useStateAction, in the utils object (alongside currentState/updateFn) for useOptimisticAction/useOptimisticStateAction. The value is captured once at mount (like React's useActionState initial state): later changes to the option are ignored, and reset() restores the mount value. The seeded shape precisely types the idle branch's result. See initResult in depth.
Supporting Docs
- execute vs executeAsync, result handling
- useStateAction in depth (decision table, formAction)
- Optimistic updates with useOptimisticAction
- useOptimisticStateAction: queued optimistic updates for overlapping writes
- Status lifecycle and all callbacks
- throwOnNavigation flag


