### Installation Source: https://next-safe-action.dev/docs/integrations/react-hook-form Install next-safe-action, React Hook Form, and the adapter package. ```APIDOC ## Installation ```bash npm install next-safe-action react-hook-form @hookform/resolvers @next-safe-action/adapter-react-hook-form ``` ``` -------------------------------- ### Install TanStack Query Adapter Source: https://next-safe-action.dev/docs/integrations/tanstack-query Install the necessary packages for integrating next-safe-action with TanStack Query. ```bash npm install next-safe-action @tanstack/react-query @next-safe-action/adapter-tanstack-query ``` -------------------------------- ### Install next-safe-action with Valibot Source: https://next-safe-action.dev/docs/quick-start Install the next-safe-action library along with Valibot for schema validation. ```bash npm install next-safe-action valibot ``` -------------------------------- ### Install next-safe-action and Better Auth Dependencies Source: https://next-safe-action.dev/docs/integrations/better-auth Install the necessary packages for next-safe-action and Better Auth integration using npm, pnpm, yarn, or bun. ```bash npm install next-safe-action better-auth @next-safe-action/adapter-better-auth ``` -------------------------------- ### Install next-safe-action with Zod Source: https://next-safe-action.dev/docs/quick-start Install the next-safe-action library along with Zod for schema validation. ```bash npm install next-safe-action zod ``` -------------------------------- ### Install next-safe-action AI Skills Source: https://next-safe-action.dev/docs/ai-skills Run this command from your project root to install all AI skills for next-safe-action. This makes the library's API and patterns discoverable by AI coding agents. ```bash npx skills add next-safe-action/skills --skill '*' ``` -------------------------------- ### Install zod-form-data Source: https://next-safe-action.dev/docs/advanced/file-uploads Install the required dependency for validating FormData. ```bash npm install zod-form-data ``` -------------------------------- ### Install React Hook Form Adapter Source: https://next-safe-action.dev/docs/integrations/react-hook-form Command to install the necessary packages for the React Hook Form integration. ```bash npm install next-safe-action react-hook-form @hookform/resolvers @next-safe-action/adapter-react-hook-form ``` -------------------------------- ### Initialize createMiddleware Source: https://next-safe-action.dev/docs/api/create-middleware Basic setup for creating a standalone middleware function. ```typescript import { createMiddleware } from "next-safe-action"; const myMiddleware = createMiddleware().define(middlewareFn); ``` -------------------------------- ### Universal Middleware Example Source: https://next-safe-action.dev/docs/api/create-middleware A middleware implementation without specific type constraints. ```typescript import { createMiddleware } from "next-safe-action"; export const loggingMiddleware = createMiddleware().define( async ({ next, metadata }) => { const start = Date.now(); const result = await next(); console.log(`Action took ${Date.now() - start}ms`, metadata); return result; } ); ``` -------------------------------- ### Implement Basic Mutation Source: https://next-safe-action.dev/docs/integrations/tanstack-query A simple example showing how to use mutationOptions within a React component. ```typescript import { useMutation } from "@tanstack/react-query"; import { mutationOptions } from "@next-safe-action/adapter-tanstack-query"; import { createUserAction } from "./actions"; function CreateUserForm() { const { mutate, isPending, isError, error, data } = useMutation( mutationOptions(createUserAction) ); return (
{ e.preventDefault(); mutate({ name: "John" }); }}> {isError && error.serverError &&

{error.serverError}

} {data &&

Created: {data.name}

}
); } ``` -------------------------------- ### Integrate with i18n libraries Source: https://next-safe-action.dev/docs/recipes/i18n Example showing how to use next-intl within an async inputSchema. ```typescript // next-intl example import { getTranslations } from "next-intl/server"; .inputSchema(async () => { const t = await getTranslations("validation"); return z.object({ name: z.string().min(2, t("name.min")), }); }) ``` -------------------------------- ### Use useAction Hook Example Source: https://next-safe-action.dev/docs/api/hooks-api Demonstrates the usage of the useAction hook to create a form that executes a createUser server action. Includes handling success and error callbacks, and managing pending states. ```typescript "use client"; import { useAction } from "next-safe-action/hooks"; import { createUser } from "./actions"; export function CreateUserForm() { const { execute, result, isPending, hasSucceeded } = useAction(createUser, { onSuccess: ({ data }) => { console.log("Created user:", data.id); }, onError: ({ error }) => { console.error("Failed:", error.serverError); }, }); return (
{ e.preventDefault(); execute({ name: "John", email: "john@example.com" }); }}> {result.validationErrors?.name &&

{result.validationErrors.name._errors[0]}

}
); } ``` -------------------------------- ### Use useStateAction Hook Example Source: https://next-safe-action.dev/docs/api/hooks-api Example of using useStateAction to update a user profile. It demonstrates form submission using `formAction`, handling success and error callbacks, and resetting the form state. ```typescript "use client"; import { useStateAction } from "next-safe-action/hooks"; import { updateProfile } from "./actions"; export function ProfileForm() { const { formAction, result, isPending, hasSucceeded, reset } = useStateAction(updateProfile, { initResult: {}, onSuccess: ({ data }) => { console.log("Profile updated:", data); }, onError: ({ error }) => { console.error("Failed:", error.serverError); }, onNavigation: ({ navigationKind }) => { console.log("Navigation:", navigationKind); }, }); return (
{result.validationErrors?.name &&

{result.validationErrors.name._errors[0]}

} {hasSucceeded &&

Saved!

}
); } ``` -------------------------------- ### Create Mutation Component with TanStack Query Source: https://next-safe-action.dev/docs/integrations/tanstack-query Use the `mutationOptions` factory from the adapter with TanStack Query's `useMutation` hook. This example shows how to handle form submission and display mutation status and results. ```typescript "use client"; import { useMutation } from "@tanstack/react-query"; import { mutationOptions, hasValidationErrors } from "@next-safe-action/adapter-tanstack-query"; import { createUser } from "./actions"; export function CreateUserForm() { const { mutate, isPending, isError, error, data } = useMutation( mutationOptions(createUser) ); return (
{ e.preventDefault(); const formData = new FormData(e.currentTarget); mutate({ name: formData.get("name") as string, email: formData.get("email") as string, }); }} > {isError && hasValidationErrors(error) && (

Validation failed

)} {isError && !hasValidationErrors(error) && (

Server error: {String(error.serverError)}

)} {data &&

Created: {data.user.name}

}
); } ``` -------------------------------- ### SafeActionClient Initialization Source: https://next-safe-action.dev/docs/api/safe-action-client Demonstrates how to create an instance of the SafeActionClient. ```APIDOC ## SafeActionClient Initialization ### Description Initializes the `SafeActionClient`, which is the core for building type-safe server actions. ### Method `createSafeActionClient()` ### Endpoint N/A (Client-side initialization) ### Request Example ```javascript import { createSafeActionClient } from "next-safe-action"; const actionClient = createSafeActionClient(); ``` ### Response Returns a new `SafeActionClient` instance. ``` -------------------------------- ### Create a minimal client Source: https://next-safe-action.dev/docs/api/create-safe-action-client Instantiate a client without any additional configuration. ```typescript import { createSafeActionClient } from "next-safe-action"; export const actionClient = createSafeActionClient(); ``` -------------------------------- ### Use useOptimisticAction Hook Example Source: https://next-safe-action.dev/docs/api/hooks-api Example of using useOptimisticAction to toggle a todo item. The UI state updates optimistically, showing the change immediately, and reverts if the toggle action fails. ```typescript "use client"; import { useOptimisticAction } from "next-safe-action/hooks"; import { toggleTodo } from "./actions"; export function TodoItem({ todo }: { todo: Todo }) { const { execute, optimisticState } = useOptimisticAction(toggleTodo, { currentState: todo, updateFn: (state, input) => ({ ...state, completed: !state.completed, }), }); return (
  • execute({ id: todo.id })} > {optimisticState.title}
  • ); } ``` -------------------------------- ### Configuration Types Source: https://next-safe-action.dev/docs/api/types Definitions for client creation and action callbacks. ```APIDOC ## Configuration Types ### CreateClientOpts Options passed to createSafeActionClient(). - **defineMetadataSchema** (function) - Optional - Metadata schema definition. - **handleServerError** (function) - Optional - Server error handler. - **defaultValidationErrorsShape** (ErrorsFormat) - Optional - Default shape for validation errors. - **throwValidationErrors** (boolean) - Optional - Whether to throw validation errors. ### ActionCallbacks Server-side callbacks passed to .action() and .stateAction(). - **onSuccess** (function) - Optional - Callback for successful execution. - **onNavigation** (function) - Optional - Callback for navigation events. - **onError** (function) - Optional - Callback for errors. - **onSettled** (function) - Optional - Callback for when the action settles. ``` -------------------------------- ### Use mutateAsync Source: https://next-safe-action.dev/docs/integrations/tanstack-query Example of using mutateAsync with error handling for validation errors. ```typescript const { mutateAsync } = useMutation(mutationOptions(createUserAction)); async function handleSubmit(formData: FormData) { try { const user = await mutateAsync({ name: formData.get("name") as string }); router.push(`/users/${user.id}`); } catch (error) { if (isActionMutationError(error) && hasValidationErrors(error)) { // handle validation errors } } } ``` -------------------------------- ### Define action clients in v7 Source: https://next-safe-action.dev/docs/migrations/v6-to-v7 Illustrates the new v7 method-based approach for defining clients and middleware using the .use() method. ```typescript 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 } }); }); ``` -------------------------------- ### Define an action in v6 Source: https://next-safe-action.dev/docs/migrations/v6-to-v7 Demonstrates the v6 syntax for creating a server action using an authenticated client. ```typescript "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, } }) ``` -------------------------------- ### Validation Error Shapes Source: https://next-safe-action.dev/docs/concepts/input-validation Examples of formatted and flattened validation error structures. ```json { validationErrors: { name: { _errors: ["String must contain at least 2 character(s)"] }, email: { _errors: ["Invalid email"] }, } } ``` ```json { validationErrors: { formErrors: [], fieldErrors: { name: ["String must contain at least 2 character(s)"], email: ["Invalid email"], }, }, } ``` -------------------------------- ### useAction with Callbacks Source: https://next-safe-action.dev/docs/execute-actions/hooks/hook-callbacks Shows how to configure the `useAction` hook with various callbacks like `onExecute`, `onSuccess`, `onError`, `onNavigation`, and `onSettled` to manage the action's lifecycle. ```javascript const { execute } = useAction(myAction, { onExecute: ({ input }) => { // Fires immediately when execute() is called console.log("Starting with input:", input); }, onSuccess: ({ data, input }) => { // Fires when the action succeeds ttoast.success(`Created: ${data.name}`); }, onError: ({ error, input }) => { // Fires when the action fails (validation or server error) if (error.validationErrors) { ttoast.error("Invalid input"); } else if (error.serverError) { ttoast.error(error.serverError); } }, onNavigation: ({ navigationKind }) => { // Fires when the action triggers a framework navigation // (redirect, notFound, forbidden, unauthorized) console.log("Navigating:", navigationKind); }, onSettled: ({ result, input }) => { // Fires after every execution (success, error, or navigation) // Like finally in a try/catch analytics.track("action_completed"); }, }); ``` -------------------------------- ### Usage with Action Clients Source: https://next-safe-action.dev/docs/api/create-middleware Demonstrates how to chain standalone middleware to an action client. ```typescript import { createSafeActionClient } from "next-safe-action"; import { loggingMiddleware } from "./middleware/logging"; import { adminGuard } from "./middleware/admin-guard"; const baseClient = createSafeActionClient().use(loggingMiddleware); // ✅ Works: adminGuard requires ctx.user, which authMiddleware provides const adminClient = baseClient .use(async ({ next }) => { const user = await getUser(); return next({ ctx: { user } }); }) .use(adminGuard); // ❌ Type error: adminGuard requires ctx.user, but baseClient doesn't have it const broken = baseClient.use(adminGuard); ``` -------------------------------- ### Basic useValidated() Example Source: https://next-safe-action.dev/docs/define-actions/middleware Use `useValidated()` after `inputSchema()` to access validated input for authorization checks. Ensure `db` and `authClient` are properly configured. ```typescript export const updatePost = authClient .inputSchema(z.object({ postId: z.string().uuid() })) .useValidated(async ({ parsedInput, ctx, next }) => { // parsedInput is typed: { postId: string } const post = await db.post.findUnique({ where: { id: parsedInput.postId } }); if (post?.authorId !== ctx.user.id) { throw new Error("Not your post"); } return next({ ctx: { post } }); }) .action(async ({ ctx }) => { // ctx.post is typed and available return { title: ctx.post.title }; }); ``` -------------------------------- ### Define Server Action with Zod Schema Source: https://next-safe-action.dev/docs/integrations/tanstack-query Define a server action using next-safe-action and Zod for input validation. This example creates a user with name and email. ```typescript "use server"; import { z } from "zod"; import { actionClient } from "@/lib/safe-action"; const createUserSchema = z.object({ name: z.string().min(2, "Name must be at least 2 characters"), email: z.string().email("Invalid email address"), }); export const createUser = actionClient .inputSchema(createUserSchema) .action(async ({ parsedInput }) => { const user = await db.user.create({ data: parsedInput }); return { user }; }); ``` -------------------------------- ### Optimistic Update Function Example Source: https://next-safe-action.dev/docs/execute-actions/hooks/useoptimisticaction A pure function used within `useOptimisticAction` to synchronously compute the new optimistic state based on the current state and action input. This function should not have side effects. ```typescript updateFn: (state, input) => { // Return a new state with the optimistic change applied return [...state, { id: "temp", ...input }]; } ``` -------------------------------- ### Build a Client Hierarchy Source: https://next-safe-action.dev/docs/concepts/action-client Create a tree of clients by chaining `.use()` with middleware. Each method returns a new instance, preserving the original. This allows for specialized clients like authenticated or admin clients. ```typescript import { createSafeActionClient } from "next-safe-action"; // Base client: error handling, logging, metadata export const actionClient = createSafeActionClient({ handleServerError(e) { console.error("Action error:", e.message); return e.message; }, }); // Authenticated client: requires valid session export const authClient = actionClient.use(async ({ next }) => { const session = await getSession(); if (!session) throw new Error("Unauthorized"); return next({ ctx: { user: session.user } }); }); // Admin client: requires admin role export const adminClient = authClient.use(async ({ next, ctx }) => { // ctx.user is available here (typed!) from the auth middleware if (ctx.user.role !== "admin") throw new Error("Forbidden"); return next({ ctx: { isAdmin: true } }); }); ``` -------------------------------- ### Define a Stateful Server Action Source: https://next-safe-action.dev/docs/guides/form-actions Use `.stateAction()` for stateful actions, which allows access to the previous result in server code. This example defines a login action with email and password validation using Zod. ```typescript "use server"; import { z } from "zod"; import { zfd } from "zod-form-data"; import { actionClient } from "@/lib/safe-action"; const schema = zfd.formData({ email: zfd.text(z.string().email()), password: zfd.text(z.string().min(8)), }); export const loginAction = actionClient .inputSchema(schema) .stateAction(async ({ parsedInput: { email, password } }) => { const user = await authenticate(email, password); return { userId: user.id }; }); ``` -------------------------------- ### createMiddleware() Source: https://next-safe-action.dev/docs/api/safe-action-client API reference for the standalone middleware factory function. ```APIDOC ## createMiddleware() ### Description Creates a standalone middleware instance that can be used with the safe action client. ### Method Function Call ``` -------------------------------- ### Handling Different Error Types in handleServerError Source: https://next-safe-action.dev/docs/api/error-classes Provides a comprehensive example of using `handleServerError` to identify and manage various error types, including ActionValidationError, ActionMetadataValidationError, and ActionOutputDataValidationError, allowing for specific error responses. ```typescript import { ActionValidationError, ActionMetadataValidationError, ActionOutputDataValidationError, } from "next-safe-action"; const actionClient = createSafeActionClient({ throwValidationErrors: true, handleServerError: (error) => { if (error instanceof ActionValidationError) { return "Please check your input."; } if (error instanceof ActionMetadataValidationError) { console.error("Developer error: invalid metadata", error.validationErrors); return "Internal error"; } if (error instanceof ActionOutputDataValidationError) { console.error("Developer error: invalid output", error.validationErrors); return "Internal error"; } return "Something went wrong."; }, }); ``` -------------------------------- ### Set up Better Auth Server Instance with Next.js Cookies Source: https://next-safe-action.dev/docs/integrations/better-auth Configure your Better Auth server instance. Use the `nextCookies()` plugin if your actions need to set cookies, such as during sign-in or sign-up. ```typescript import { betterAuth } from "better-auth"; import { nextCookies } from "better-auth/next-js"; export const auth = betterAuth({ // ...your config (database, plugins, etc.) plugins: [ // ...other plugins nextCookies(), // must be the last plugin in the array ], }); ``` -------------------------------- ### Manually Return Validation Errors Source: https://next-safe-action.dev/docs/advanced/custom-validation-errors Use `returnValidationErrors` to return validation errors from server code, for example, when business logic checks fail. This function throws internally, ensuring subsequent code does not execute. ```typescript import { returnValidationErrors } from "next-safe-action"; export const signUp = actionClient .inputSchema(z.object({ email: z.string().email(), username: z.string().min(3), })) .action(async ({ parsedInput }) => { // Check business logic const emailExists = await db.user.findByEmail(parsedInput.email); if (emailExists) { return returnValidationErrors(schema, { email: { _errors: ["Email already registered"] }, }); } const usernameExists = await db.user.findByUsername(parsedInput.username); if (usernameExists) { return returnValidationErrors(schema, { username: { _errors: ["Username taken"] }, }); } // Create user... }); ``` -------------------------------- ### createMiddleware() API Reference Source: https://next-safe-action.dev/docs/define-actions/action-utils API reference for the createMiddleware() function. ```APIDOC ## createMiddleware() ### Description API reference for the `createMiddleware()` function. ### Method Not applicable (function reference) ### Endpoint Not applicable (function reference) ``` -------------------------------- ### Action builder method chaining Source: https://next-safe-action.dev/docs/api/safe-action-client Demonstrates the recommended order for chaining builder methods. ```typescript actionClient .use(middleware) // 1. Add pre-validation middleware (repeatable) .metadata(data) // 2. Set metadata (if schema defined) .inputSchema(schema) // 3. Define input validation .bindArgsSchemas([...]) // 4. Define bind args (optional, must be before useValidated) .useValidated(middleware) // 5. Add post-validation middleware (repeatable, requires schema) .outputSchema(schema) // 6. Define output validation (optional) .action(fn, utils) // 7. Define server code (terminal) ``` -------------------------------- ### Handle input validation errors Source: https://next-safe-action.dev/docs/concepts/action-result This example demonstrates how to define an action with input validation. If the input does not match the schema, 'validationErrors' will be populated, and the action's core logic will not execute. This is useful for enforcing data integrity before server-side processing. ```typescript export const createUser = actionClient .inputSchema(z.object({ name: z.string().min(2), email: z.string().email(), })) .action(async ({ parsedInput }) => { // This code doesn't run if validation fails }); // On the client (with invalid input): const result = await createUser({ name: "", email: "bad" }); // result.validationErrors → { // name: { _errors: ["String must contain at least 2 character(s)"] }, // email: { _errors: ["Invalid email"] } // } ``` -------------------------------- ### Initialize a safe action client Source: https://next-safe-action.dev/docs/api/create-safe-action-client Basic usage of createSafeActionClient to instantiate a new client. ```typescript import { createSafeActionClient } from "next-safe-action"; const actionClient = createSafeActionClient(opts); ``` -------------------------------- ### Using useAction hook for reactive result handling Source: https://next-safe-action.dev/docs/concepts/action-result This example shows how to use the `useAction` hook for managing action results reactively. It utilizes `onSuccess` and `onError` callbacks to handle different outcomes, including specific checks for `validationErrors` and `serverError` within the `onError` callback. ```typescript const { execute } = useAction(myAction, { onSuccess: ({ data }) => { /* ... */ }, onError: ({ error }) => { if (error.validationErrors) { /* ... */ } if (error.serverError) { /* ... */ } }, }); ``` -------------------------------- ### useAction with Callbacks Source: https://next-safe-action.dev/docs/execute-actions/hooks/usestateaction Shows how to use the `useAction` hook with various callbacks like `onExecute`, `onSuccess`, `onError`, `onNavigation`, and `onSettled` to manage the action's lifecycle and side effects. ```javascript const { execute } = useAction(myAction, { onExecute: ({ input }) => { // Fires immediately when execute() is called console.log("Starting with input:", input); }, onSuccess: ({ data, input }) => { // Fires when the action succeeds ttoast.success(`Created: ${data.name}`); }, onError: ({ error, input }) => { // Fires when the action fails (validation or server error) if (error.validationErrors) { ttoast.error("Invalid input"); } else if (error.serverError) { ttoast.error(error.serverError); } }, onNavigation: ({ navigationKind }) => { // Fires when the action triggers a framework navigation // (redirect, notFound, forbidden, unauthorized) console.log("Navigating:", navigationKind); }, onSettled: ({ result, input }) => { // Fires after every execution (success, error, or navigation) // Like finally in a try/catch analytics.track("action_completed"); }, }); ``` -------------------------------- ### Adding Middleware with .use() Source: https://next-safe-action.dev/docs/api/safe-action-client Explains how to add middleware functions to the action execution chain using the .use() method. ```APIDOC ## .use() ### Description Adds a middleware function to the action execution chain. This method cannot be called after `useValidated()`. ### Method `client.use(middlewareFn)` ### Endpoint N/A (Method on `SafeActionClient` instance) ### Parameters #### Request Body - **middlewareFn** (MiddlewareFn) - Optional - The middleware function to execute. It receives `clientInput`, `bindArgsClientInputs`, `ctx`, `metadata`, and a `next` function. ### Request Example ```javascript const actionClient = createSafeActionClient(); const authClient = actionClient.use(async ({ next }) => { const session = await getSession(); if (!session) throw new Error("Unauthorized"); return next({ ctx: { user: session.user } }); }); // authClient now has ctx: { user: User } ``` ### Response Returns a new `SafeActionClient` instance with `NextCtx` merged into the context type. ``` -------------------------------- ### Initialize SafeActionClient Source: https://next-safe-action.dev/docs/api/safe-action-client Create a new instance of the SafeActionClient to begin building server actions. ```typescript import { createSafeActionClient } from "next-safe-action"; const actionClient = createSafeActionClient(); ``` -------------------------------- ### Configure action client options Source: https://next-safe-action.dev/docs/define-actions/create-the-client Customizes error handling, metadata schemas, and validation error shapes during client initialization. ```typescript import { createSafeActionClient } from "next-safe-action"; export const actionClient = createSafeActionClient({ // Customize how server errors are handled and what's sent to the client handleServerError(e) { console.error("Action error:", e.message); return "Something went wrong"; }, // Define a metadata schema (makes metadata type-safe) defineMetadataSchema() { return z.object({ actionName: z.string(), }); }, // Change the default validation error shape defaultValidationErrorsShape: "flattened", }); ``` -------------------------------- ### Method Chaining Order Source: https://next-safe-action.dev/docs/api/safe-action-client Illustrates the recommended order for chaining methods when configuring safe actions. ```APIDOC ## Method chaining order ### Description Methods can be called in any order with a few constraints: `use()` must come before `useValidated()`, schemas must come before `useValidated()`, and the final call must be `.action()` / `.stateAction()`. ### Typical order ```javascript actionClient .use(middleware) // 1. Add pre-validation middleware (repeatable) .metadata(data) // 2. Set metadata (if schema defined) .inputSchema(schema) // 3. Define input validation .bindArgsSchemas([...]) // 4. Define bind args (optional, must be before useValidated) .useValidated(middleware) // 5. Add post-validation middleware (repeatable, requires schema) .outputSchema(schema) // 6. Define output validation (optional) .action(fn, utils) // 7. Define server code (terminal) ``` ``` -------------------------------- ### Setting Metadata with .metadata() Source: https://next-safe-action.dev/docs/api/safe-action-client Describes how to attach metadata to a server action using the .metadata() method. ```APIDOC ## .metadata() ### Description Sets metadata for the action. This method is only available when a metadata schema is defined via `defineMetadataSchema` in `createSafeActionClient`. It must be called before `.action()` or `.stateAction()`. ### Method `client.metadata(data)` ### Endpoint N/A (Method on `SafeActionClient` instance) ### Parameters #### Request Body - **data** (Metadata) - Optional - The metadata to attach to the action, inferred from the metadata schema. ### Request Example ```javascript const myAction = actionClient .metadata({ actionName: "createUser" }) .action(async ({ parsedInput, metadata }) => { console.log(metadata.actionName); // "createUser" }); ``` ### Response Returns a new `SafeActionClient` instance with the provided metadata. ``` -------------------------------- ### Middleware .use() Method Source: https://next-safe-action.dev/docs/guides/middleware Defines the structure and arguments for middleware functions used to wrap server actions. ```APIDOC ## .use(middlewareFunction) ### Description Registers a middleware function that executes before and after the server action code. Middleware can be applied at the client instance level or the individual action level. ### Parameters #### Middleware Arguments - **clientInput** (unknown) - The raw, unvalidated input from the client. - **bindArgsClientInputs** (unknown[]) - Raw bound argument values. - **ctx** (Ctx) - Accumulated context from previous middleware. - **metadata** (MD) - Action metadata (if defineMetadataSchema is configured). - **next** (Function) - A function that calls the next middleware or the server code. Accepts an optional object `{ ctx: { ... } }` to merge new data into the context. ### Request Example ```typescript .use(async ({ next, ctx, metadata }) => { // Logic before next() const result = await next({ ctx: { newKey: 'value' } }); // Logic after next() return result; }) ``` ``` -------------------------------- ### Define action clients in v6 Source: https://next-safe-action.dev/docs/migrations/v6-to-v7 Shows the legacy approach to creating base and authenticated action clients using a single middleware configuration. ```typescript 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 }; }, }); ``` -------------------------------- ### useAction Hook - execute vs executeAsync Source: https://next-safe-action.dev/docs/execute-actions/hooks/hook-callbacks Explains the difference between `execute` and `executeAsync` methods provided by the `useAction` hook. ```APIDOC ## `execute` vs `executeAsync` ### Description This section clarifies the distinct use cases and return behaviors of the `execute` and `executeAsync` methods offered by the `useAction` hook. ### `execute(input)` - **Returns**: `void` - **Use when**: You prefer a fire-and-forget approach, handling results reactively through the `result` property or via callbacks. ### `executeAsync(input)` - **Returns**: `Promise` - **Use when**: You need to explicitly `await` the action's result, which is useful for sequential operations or conditional logic based on the outcome. ### Example Usage ```javascript // Fire-and-forget, result is available reactively execute({ name: "Alice" }); // Await the result: useful for sequential operations const result = await executeAsync({ name: "Alice" }); if (result?.data) { await executeAsync({ name: "Bob" }); } ``` ### Error Handling - `executeAsync` will throw a server error if the action fails, requiring a `try/catch` block for handling. - `execute` never throws; errors are always captured within the `result` object. ``` -------------------------------- ### Navigation Handling with `next/navigation` Source: https://next-safe-action.dev/docs/migrations/v7-to-v8 Demonstrates how to handle navigation events within actions and components using `onNavigation` callbacks. The `status` changes to `hasNavigated` and `onSuccess` is replaced by `onNavigation`. ```typescript import { useAction } from "next-safe-action/hooks"; import { redirect } from "next/navigation"; // In the action definition const action = actionClient.action( async () => { redirect("/"); }, { onNavigation: async ({ navigationKind }) => { // Do something with the navigation... }, } ); // In the component const { execute, status } = useAction(action, { onNavigation: ({ navigationKind }) => { // Do something with the navigation... }, }); ``` -------------------------------- ### createSafeActionClient() Source: https://next-safe-action.dev/docs/api/create-safe-action-client Initializes a new safe action client instance for defining type-safe server actions. ```APIDOC ## createSafeActionClient() ### Description Creates a new safe action client instance. This is the entry point for defining type-safe server actions. ### Parameters - **handleServerError** (function) - Optional - A function to handle server errors, receiving (error: Error, utils: ServerErrorFunctionUtils) and returning a MaybePromise. - **defineMetadataSchema** (function) - Optional - A function that returns a MetadataSchema. - **defaultValidationErrorsShape** (string) - Optional - Defines the shape of validation errors: "formatted" | "flattened". - **throwValidationErrors** (boolean) - Optional - Whether to throw validation errors. ### Return Value Returns a `SafeActionClient` instance with all chainable methods available. ### Request Example ```typescript import { createSafeActionClient } from "next-safe-action"; export const actionClient = createSafeActionClient({ handleServerError: (error, { metadata }) => { return "Something went wrong."; }, }); ``` ``` -------------------------------- ### useAction Hook - Options and Callbacks Source: https://next-safe-action.dev/docs/execute-actions/hooks/hook-callbacks Details the available options and callbacks that can be passed to the `useAction` hook for customizing behavior and handling lifecycle events. ```APIDOC ## useAction Hook - Options and Callbacks ### Description Customize the behavior of the `useAction` hook by providing an options object with various callbacks to intercept different stages of the action lifecycle. ### Usage ```javascript const { execute } = useAction(myAction, { onExecute: ({ input }) => { // Fires immediately when execute() is called console.log("Starting with input:", input); }, onSuccess: ({ data, input }) => { // Fires when the action succeeds ttoast.success(`Created: ${data.name}`); }, onError: ({ error, input }) => { // Fires when the action fails (validation or server error) if (error.validationErrors) { ttoast.error("Invalid input"); } else if (error.serverError) { ttoast.error(error.serverError); } }, onNavigation: ({ navigationKind }) => { // Fires when the action triggers a framework navigation // (redirect, notFound, forbidden, unauthorized) console.log("Navigating:", navigationKind); }, onSettled: ({ result, input }) => { // Fires after every execution (success, error, or navigation) // Like finally in a try/catch analytics.track("action_completed"); }, }); ``` ### Available Callbacks - **`onExecute`**: Called right before the action is executed. - **`onSuccess`**: Called when the action completes successfully. - **`onError`**: Called when the action fails due to validation or server errors. - **`onNavigation`**: Called when the action triggers a navigation event. - **`onSettled`**: Called after the action has settled (regardless of outcome), similar to a `finally` block. ``` -------------------------------- ### Execute vs ExecuteAsync Usage Source: https://next-safe-action.dev/docs/execute-actions/hooks/hook-callbacks Illustrates the difference between `execute` (fire-and-forget) and `executeAsync` (awaitable) for handling server action results, including sequential execution patterns. ```javascript // Fire-and-forget, result is available reactively execute({ name: "Alice" }); // Await the result: useful for sequential operations const result = await executeAsync({ name: "Alice" }); if (result?.data) { await executeAsync({ name: "Bob" }); } ``` -------------------------------- ### Define CreateClientOpts type Source: https://next-safe-action.dev/docs/api/types Represents the options passed to createSafeActionClient(). ```typescript type CreateClientOpts = { defineMetadataSchema?: () => MetadataSchema; handleServerError?: HandleServerErrorFn; defaultValidationErrorsShape?: ErrorsFormat; throwValidationErrors?: boolean; }; ``` -------------------------------- ### Demonstrate client immutability Source: https://next-safe-action.dev/docs/define-actions/create-the-client Shows that chainable methods return new instances rather than modifying the original client. ```typescript // This doesn't modify actionClient, it returns a new instance const authClient = actionClient.use(authMiddleware); // actionClient still has no middleware // authClient has authMiddleware ``` -------------------------------- ### Implement Navigation in a Server Action Source: https://next-safe-action.dev/docs/advanced/framework-errors Demonstrates using redirect within a server action; next-safe-action handles the internal error automatically. ```typescript "use server"; import { redirect } from "next/navigation"; import { actionClient } from "@/lib/safe-action"; export const loginAction = actionClient .inputSchema(loginSchema) .action(async ({ parsedInput }) => { const user = await authenticate(parsedInput); if (!user) { // Return validation error, NOT a framework error return returnValidationErrors(loginSchema, { _errors: ["Invalid credentials"], }); } // This is a framework error: triggers navigation redirect("/dashboard"); }); ``` -------------------------------- ### Define an action in v7 Source: https://next-safe-action.dev/docs/migrations/v6-to-v7 Shows the v7 fluent API syntax using .schema() and .action() methods. ```typescript "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, } }); ``` -------------------------------- ### Importing All Type Utilities Source: https://next-safe-action.dev/docs/api/type-utilities Import all available type utilities from the main entry point of next-safe-action. ```typescript import type { InferSafeActionFnInput, InferSafeActionFnResult, InferMiddlewareFnNextCtx, InferCtx, InferMetadata, InferServerError, } from "next-safe-action"; ``` -------------------------------- ### Action Client Methods Source: https://next-safe-action.dev/docs/api/safe-action-client Reference for the method chaining API available on the action client. ```APIDOC ## Action Client Methods ### Description Methods available for chaining when configuring actions. ### Methods - **.use()**: Adds middleware to the action. - **.useValidated()**: Adds validated middleware to the action. - **.metadata()**: Sets metadata for the action. - **.inputSchema()**: Defines the input validation schema. - **.outputSchema()**: Defines the output validation schema. - **.bindArgsSchemas()**: Binds argument schemas. - **.action()**: Defines the main action logic. - **.stateAction()**: Defines a stateful action. ``` -------------------------------- ### Basic Usage of useAction Hook Source: https://next-safe-action.dev/docs/execute-actions/hooks/hook-callbacks Demonstrates the fundamental integration of the `useAction` hook within a React Client Component to execute a server action and display its result or loading state. ```javascript "use client"; import { useAction } from "next-safe-action/hooks"; import { myAction } from "./actions"; export default function MyComponent() { const { execute, result, status, isExecuting } = useAction(myAction); return (
    {result.data &&

    Success: {JSON.stringify(result.data)}

    } {result.serverError &&

    Error: {result.serverError}

    }
    ); } ``` -------------------------------- ### Action Client Methods Source: https://next-safe-action.dev/docs/define-actions/instance-methods Details on methods available for action clients, including .use(), .useValidated(), .metadata(), .inputSchema(), .outputSchema(), .bindArgsSchemas(), .action(), and .stateAction(). ```APIDOC ## Action Client Methods ### Description This section details various methods available for action clients, including middleware application, schema definition, and action execution. ### Methods - `.use()` - `.useValidated()` - `.metadata()` - `.inputSchema()` - `.outputSchema()` - `.bindArgsSchemas()` - `.action()` - `.stateAction()` ### Endpoint Not applicable (method references within a client instance) ### Parameters Parameters for each method are not specified in the provided text. ### Request Example Not applicable (method references within a client instance) ### Response Not applicable (method references within a client instance) ``` -------------------------------- ### Create an Authenticated Action Client with Better Auth Middleware Source: https://next-safe-action.dev/docs/integrations/better-auth Create a public and an authenticated action client. The authenticated client uses `betterAuthMiddleware()` to enforce authentication and inject user/session data. ```typescript import { createSafeActionClient } from "next-safe-action"; import { betterAuthMiddleware } from "@next-safe-action/adapter-better-auth"; import { auth } from "./auth"; // Public action client (no auth required) export const actionClient = createSafeActionClient(); // Authenticated action client export const authClient = actionClient.use(betterAuthMiddleware(auth)); ``` -------------------------------- ### Basic useAction Hook Usage Source: https://next-safe-action.dev/docs/execute-actions/hooks/usestateaction Demonstrates the basic integration of the `useAction` hook in a Client Component to execute a server action and display its result or errors. ```javascript "use client"; import { useAction } from "next-safe-action/hooks"; import { myAction } from "./actions"; export default function MyComponent() { const { execute, result, status, isExecuting } = useAction(myAction); return (
    {result.data &&

    Success: {JSON.stringify(result.data)}

    } {result.serverError &&

    Error: {result.serverError}

    }
    ); } ``` -------------------------------- ### Define Server Actions with Standard Schema Libraries Source: https://next-safe-action.dev/docs/integrations/standard-schema Use .inputSchema() to pass schemas from Zod, Valibot, or ArkType to the action client. The API remains consistent regardless of the chosen library. ```typescript "use server"; import { z } from "zod"; import { actionClient } from "@/lib/safe-action"; export const createUser = actionClient .inputSchema( z.object({ name: z.string().min(2), email: z.string().email(), }) ) .action(async ({ parsedInput }) => { // parsedInput: { name: string, email: string } }); ``` ```typescript "use server"; import * as v from "valibot"; import { actionClient } from "@/lib/safe-action"; export const createUser = actionClient .inputSchema( v.object({ name: v.pipe(v.string(), v.minLength(2)), email: v.pipe(v.string(), v.email()), }) ) .action(async ({ parsedInput }) => { // parsedInput: { name: string, email: string } }); ``` ```typescript "use server"; import { type } from "arktype"; import { actionClient } from "@/lib/safe-action"; export const createUser = actionClient .inputSchema( type({ name: "string >= 2", email: "string.email", }) ) .action(async ({ parsedInput }) => { // parsedInput: { name: string, email: string } }); ``` -------------------------------- ### Demonstrate type constraint errors Source: https://next-safe-action.dev/docs/advanced/standalone-middleware TypeScript will flag usage of middleware on clients that do not meet the required context shape. ```typescript // ✅ Works: authClient has ctx.user from auth middleware const adminClient = authClient.use(adminGuard); // ❌ Type error: actionClient doesn't have ctx.user const broken = actionClient.use(adminGuard); ``` -------------------------------- ### Set up async schema for i18n Source: https://next-safe-action.dev/docs/recipes/i18n Define an async function in inputSchema to load translations and return a Zod schema. ```typescript "use server"; import { z } from "zod"; import { actionClient } from "@/lib/safe-action"; import { getTranslations } from "@/lib/i18n"; export const createUser = actionClient .inputSchema(async () => { // Load translations for the current locale const t = await getTranslations("validation"); return z.object({ name: z.string().min(2, t("name.tooShort")), email: z.string().email(t("email.invalid")), age: z.number().min(18, t("age.tooYoung")), }); }) .action(async ({ parsedInput }) => { // parsedInput is typed based on the returned schema await db.user.create({ data: parsedInput }); }); ``` -------------------------------- ### Initialize mutationOptions Source: https://next-safe-action.dev/docs/integrations/tanstack-query Basic usage of mutationOptions to create a UseMutationOptions object for use with useMutation. ```typescript import { mutationOptions } from "@next-safe-action/adapter-tanstack-query"; const options = mutationOptions(safeActionFn, opts?); ``` -------------------------------- ### Define Actions Using Different Clients Source: https://next-safe-action.dev/docs/concepts/action-client Use the appropriate client instance to define actions with specific requirements, such as public access, authentication, or admin privileges. Middleware and input schemas are applied based on the client used. ```typescript "use server"; // Public action: no auth needed export const getPublicData = actionClient .action(async () => { /* ... */ }); // Authenticated action: user must be logged in export const updateProfile = authClient .inputSchema(profileSchema) .action(async ({ parsedInput, ctx }) => { // ctx.user is typed and available }); // Admin action: user must be admin export const deleteUser = adminClient .inputSchema(z.object({ userId: z.string() })) .action(async ({ parsedInput, ctx }) => { // ctx.user and ctx.isAdmin are both available }); ``` -------------------------------- ### Create Server Component to Fetch Todos Source: https://next-safe-action.dev/docs/execute-actions/hooks/useoptimisticaction A Server Component that fetches the list of todos from the database. This data is then passed as a prop to the Client Component for rendering. ```typescript import { db } from "@/lib/db"; import { TodoList } from "./todo-list"; export default async function TodosPage() { const todos = await db.todo.findMany(); return ; } ```