Migration from v6 to v7
What's new?
Well, pretty much everything. Version 7 now works using methods; you might be familiar with this design if you have worked with tRPC or Kysely. A complete rewrite of the library in this direction was needed to vastly improve next-safe-action's APIs, and ensure that future versions will not break them (unless React/Next.js APIs change under the hood). The new design is much more resilient, powerful and flexible.
TL;DR
But please still read this migration guide carefully before upgrading to v7.
Assuming you're using Zod, in previous versions, you'd define an auth action client and then an action like this:
import { createSafeActionClient } from "next-safe-action";
import { cookies } from "next/headers";
// Base client
export const baseActionClient = createSafeActionClient();
// Auth client
export const authActionClient = createSafeActionClient({
async middleware(parsedInput) {
const session = cookies().get("session")?.value;
if (!session) {
throw new Error("Session not found!");
}
const userId = await getUserIdFromSessionId(session);
if (!userId) {
throw new Error("Session is not valid!");
}
return { userId };
},
});
"use server";
import { authActionClient } from "@/lib/safe-action";
import { z } from "zod";
export const editProfile = authActionClient(z.object({ username: z.string() }), async ({ username }, { ctx: { userId } }) => {
await saveNewUsernameInDb(userId, username);
return {
updated: true,
}
})
The same behavior can be achieved in v7 with the following refectored code:
import { createSafeActionClient } from "next-safe-action";
import { cookies } from "next/headers";
// Base client
export const actionClient = createSafeActionClient();
// Auth client
export const authActionClient = actionClient.use(async ({ next, ctx }) => {
const session = cookies().get("session")?.value;
if (!session) {
throw new Error("Session not found!");
}
const userId = await getUserIdFromSessionId(session);
if (!userId) {
throw new Error("Session is not valid!");
}
return next({ ctx: { userId } });
});
"use server";
import { authActionClient } from "@/lib/safe-action";
import { z } from "zod";
export const editProfile = authActionClient
.schema(z.object({ username: z.string() }))
.action(async ({ parsedInput: { username }, ctx: { userId } }) => {
await saveNewusernameInDb(userId, username)
return {
updated: true,
}
});
New features
Allow setting validation errors in action server code function
Sometimes it's useful to set custom validation errors in the action server code function, for example when the user wants to log in, but there was a problem with the email or password fields. next-safe-action v7 introduces a new function called returnValidationErrors
that allows you to do that.
Support schema nested objects validation
Before v7, next-safe-action allowed you to define schemas with nested objects, but validation errors were not correctly set for nested fields. Version 7 of the library changes the returned errors to be an object with nested fields, that emulates Zod's format
method.
Support middleware chaining
This is a core change in next-safe-action v7. In previous versions, you could define just one "monolithic" middleware at the instance level. So, the previous workflow was to define multiple safe action clients, each one with its own middleware.
With version 7, you can chain multiple middleware functions using the use
method, both at the instance level and at the action level. This is explained in detail in the middleware page of the documentation. The new design is much more flexible and powerful, allowing you to do things that just couldn't be done before, such as extending context, logging action execution, integrating with third party systems for error reporting, etc.
Generic type for serverError
The serverError
property of the action result object is now of generic type. By default it's a string
with a default value of "Something went wrong while executing the operation.". You can customize error value and type using the handleReturnedServerError
initialization function, just like pre-v7. Basically, what you return from that function is what serverError
will be on the client.
Support binding additional arguments
Next.js allows you to pass additional arguments to the action using JavaScript bind
method. This approach has the advantage of supporting progressive enhancement.
next-safe-action v7 supports bind arguments via the bindArgsSchemas
method.
Support setting default validation errors shape per instance
By default, next-safe-action v7 returns validation errors in an object of the same shape as Zod's format
method. You can override this behavior globally by setting the defaultValidationErrorsShape
optional property to flattened
in createSafeActionClient
method. Doing so, the validation errors are returned in the shape of the Zod's format
method. If you need a custom format for a specific action, you can override the default shape using the handleValidationErrorsShape
and handleBindArgsValidationErrorsShape
optional functions in schema
and bindArgsSchemas
methods, as explained below.
Support custom validation errors shape
As already said above, by default version 7 now returns validation errors in the same format of the Zod's format
method.
This is customizable by using the handleValidationErrorsShape
/handleBindArgsValidationErrorsShape
optional functions in schema
/bindArgsSchemas
methods. Check out this page for more information. For example, if you need to work with flattened errors for a specific action, next-safe-action conveniently provides two functions to do that: flattenValidationErrors
and flattenBindArgsValidationErrors
.
Allow calling action
method without schema
Sometimes it's not necessary to define an action with input. In this case, you can omit the schema
method and use directly the action
/stateAction
method.
Support passing schema via async function
When working with i18n solutions, often you'll find implementations that require awaiting a getTranslations
function in order to get the translations, that then get passed to the schema. Starting from version 7, next-safe-action allows you to pass an async function to the schema
method, that returns a promise of type Schema
. More information about this feature can be found in this discussion on GitHub and in the i18n recipe page.
Support action execution callbacks
It's sometimes useful to be able to execute custom logic on the server side after an action succeeds or fails. Starting from version 7, next-safe-action allows you to pass action callbacks when defining an action. More information about this feature can be found here.
Support stateful actions using React useActionState
hook
React added a hook called useActionState
that replaces the previous useFormState
hook and improves it. next-safe-action v7 uses it under the hood in the exported useStateAction
hook, that keeps track of the state of the action execution.
Note that this hook expects as argument actions defined using the stateAction
method, and not the usual action
method. Find more information about these two methods here.
The useActionState
hook requires Next.js >= 15 to work, because previous versions do not support the React's useActionState
hook that is used under the hood. In the meantime, you can use the stateAction
method manually with React 18's useFormState
hook.
The useActionState
hook is exported from next-safe-action/stateful-hooks
path, unlike the other two hooks. This is because it uses React 19 features and would cause build errors in React 18.
Return input from hooks
Sometimes it's useful to access the input passed to an action when using hooks. Starting from version 7, input
property is returned from hooks.
Return shorthand statuses from hooks
Starting from version 7, isIdle
, isExecuting
, hasSucceeded
and hasErrored
are returned from hooks, in addition to the status
property. This is the same behavior of next-safe-action pre-v4 and very similar to the TanStack Query API.
Return executeAsync
from useAction
and useOptimisticAction
hooks
Sometimes it's useful to await the result of an action execution when using actions via hooks. Starting from version 7, executeAsync
is returned from useAction
and useOptimisticAction
hooks. It's essentially the same as the original safe action function, with the added benefits of the hooks execution behavior. Note that it's currently not possible to return this function from the useStateAction
hook, due to internal React limitations.
Refactors
serverCodeFn
signature
Previously, serverCodeFn
had two arguments: parsedInput
and ctx
. Now, it only has one argument, which is an object that contains parsedInput
and ctx
, and other useful properties. In the case of stateAction
method, serverCodeFn
also has an additional argument, which is an object that contains the previous result of the action. Find more information about serverCodeFn
here.
useOptimisticAction
signature
The function signature for useOptimisticAction
has been updated to be much more clear and readable. Before, you had to pass currentState
and updateFn
as the second and third argument of the hook. Now, the first argument is the safe action, and additional required and optional properties are placed inside the second argument of the hook, which is an object.
Other than that, now currentState
is unlinked from the safe action's return value. The action purpose in optimistic state updates is just to make mutations of data. Then, the fresh data is refetched from the parent Server Component, so it didn't make sense to lock the type of currentState
to the action's return type. This is explained in detail here and here.
Find more information about the updated useOptimisticAction
hook here.
Hook callbacks arguments
Previously, there were multiple arguments in hook callbacks. Now, metadata is passed inside a single object that is the first argument of each function. Find more information about the updated callbacks here.
Action metadata
In version 6, you could pass metadata to actions via the third argument of the safe action function, after serverCodeFn
. In version 7, there's a dedicated metadata
method that lets you define useful data for the action execution. This data can then be accessed in middleware functions and serverCodeFn
. Find more information about the metadata
method here.
Internal changes
TypeSchema update
TypeSchema was updated to v0.13, so now, if you want to use a validation library other than Zod, you also need to install the related TypeSchema adapter.
Requirements
next-safe-action version 7 requires Next.js 14 and React 18.2.0 or later to work. For useActionState
hook, the minimum required Next.js version is 15, since previous versions don't support the React's useStateAction
hook that is used under the hood. The useActionState
hook is exported from next-safe-action/stateful-hooks
path.
What about v6?
You can still keep using version 6 and eventually upgrade to version 7. Note that version 6 is frozen and no new features will be released in the future for it. v6 documentation can still be found here.