npm / next-safe-action
next-safe-action
What changed in next-safe-action 8, from its official release notes, upgrade guides and advisories. Sources checked 2026-10-22.
12 breaking2 security5 deprecations8 new APIs and options5 behavior and defaults18 fixes and other
See which of these apply to your code, ranked, in your agent:
npx @unstale/cli initnext-safe-action 850 changes
Breaking changes 12
Queued dispatches marked stale by reset() no longer call the action or write to the server; their executeAsync promises resolve with {} and their callbacks do not fire. Applies to useStateAction and useOptimisticStateAction.
Adds an optimistic hook for stateful actions. Dispatches queue, each receives the previous result, and optimistic updates can accumulate instead of using last-write-wins behavior.
Output schema transforms and defaults are applied to returned data and the onSuccess callback, rather than using the raw action return value.
In useAction and useOptimisticAction, reset() invalidates in-flight executions so pending responses cannot repopulate hook state.
All hooks report idle status immediately after reset, even while an uncancellable in-flight execution is still settling.
Passing a non-JSON-serializable payload now throws a clear error instead of leaking a raw TypeError.
The library no longer has deepmerge-ts as a runtime dependency; it now ships with zero runtime dependencies.
Action results now prioritize validationErrors, then serverError, then data, so only one of these fields is populated when multiple errors occur.
The transitioning value was removed from the HookActionStatus union; the isTransitioning boolean on the hook return object is unchanged.
Hook return types are now discriminated unions keyed on status; utilities such as Pick, Omit, and Partial distribute over the union, and constructed values must match one branch.
Callbacks now fire immediately after an action completes.
Navigation errors are thrown immediately to support Next.js Cache Components.
Security 2
Validation issue paths are traversed safely, and hostile keys are stored as own properties so they cannot modify the global prototype.
Field keys are assigned safely to prevent hostile keys from modifying the global prototype.
Deprecations 5
Renames the exported type SafeActionUtils; SafeActionUtils remains available as a deprecated alias.
Renames the exported type HookSafeActionFn; HookSafeActionFn remains available as a deprecated alias.
Renames the exported type HookSafeStateActionFn; HookSafeStateActionFn remains available as a deprecated alias.
Renames the exported type StateServerCodeFn; StateServerCodeFn remains available as a deprecated alias.
Renames the exported type DVES; DVES remains available as a deprecated alias.
New APIs and options 8
Adds an option to useAction and useOptimisticAction that seeds the hook with an initial result; reset() restores it.
Adds a utility for returning typed, expected server errors to the client as result.serverError, bypassing handleServerError.
Adds post-validation middleware with type-safe context.
Adds a flag to internal hooks, defaulting to false; when true, navigation functions such as forbidden() and notFound() navigate to an error page, and onNavigation and onSettled callbacks cannot be used.
Exported from the core package so developers can use it.
Exported from the core package so developers can use it.
Exported from the core package so developers can use it.
The inputSchema method now provides access to clientInput.
Behavior and default changes 5
Confirmed state uses successful action data when available, otherwise currentState; a revalidated currentState can supersede an in-flight dispatch result.
Validation and server errors roll back to the last confirmed domain state, and failed dispatches pass that state as prevResult.data to keep queued writes based.
useStateAction supports overlapping executeAsync invocations, with each promise settling with its own execution result.
SafeActionResult is now a discriminated union: checking data, serverError, or validationErrors narrows the other fields to undefined.
Structured inputs are now extracted from schemas instead of being derived by iterating client inputs.
Fixes and other changes 18
In useAction, useOptimisticAction, and useStateAction, onNavigation and onSettled now fire exactly once for a single redirect.
Stops leaking an unhandled promise rejection when the action call itself rejects; the error still reaches the caller through status, onError with thrownError, and the executeAsync rejection.
Stops leaking an unhandled promise rejection when the action call itself rejects; the error still reaches the caller through status, onError with thrownError, and the executeAsync rejection.
When currentState commits mid-queue, payloads from already-settled dispatches stop being applied again, preventing optimistic state from duplicating changes.
A currentState arriving while an action is running no longer discards that action’s data when building the next queued dispatch’s server base; the result follows write order.
A raw thrown error now settles promises for dispatches queued behind it; previously React clearing the action queue could leave those promises unresolved.
A throw from a dispatch made stale by reset no longer cancels fresh queued work.
A synchronous throw from a user callback no longer escapes before dispatch enqueueing, which could leave executeAsync pending forever.
When an action is dispatched inside a React transition, dispatch-time state now also applies from a microtask so it can commit immediately; this fixes pending status, input visibility, and callback timing in the action hooks.
Results resolving to undefined are normalized where stored, preventing onSuccess and onSettled from firing again on unrelated re-renders.
In useStateAction, reset() marks already-dispatched executions stale so they cannot clear reset state, consume the initResult baseline, or repopulate errors.
When called inside a Next.js 'use cache' scope with cacheComponents enabled, validation errors are returned as validationErrors instead of a generic server error.
Hook callbacks now fire once per action execution instead of replaying on every restore from the Next.js router bfcache.
The deprecation notice was removed from the hook.
The hook's code was fixed.
Reverses the
_errors/object check order and skips_errorskeys as nested objects.Rapid execute or executeAsync calls now update UI state only from the latest request, preventing a slower earlier response from overwriting a later result; pre-transition state updates are synchronous.
Checks ActionServerValidationError before the serverErrorHandled guard.