{"id":11393,"library":"next-safe-action","title":"Type-Safe Next.js Server Actions","description":"next-safe-action is a library designed for Next.js projects to create type-safe and validated Server Actions. It leverages modern Next.js, React, and TypeScript features to ensure end-to-end type safety from client-side component calls to server-side action execution. The current stable version is 8.5.2, with minor and patch releases occurring frequently to refine types, add features, and improve developer experience. Key differentiators include robust input/output validation, a flexible middleware system for authorization or logging, advanced server error handling, and support for optimistic updates, making it a powerful tool for building reliable and predictable data mutations in Next.js applications.","status":"active","version":"8.5.2","language":"javascript","source_language":"en","source_url":"https://github.com/next-safe-action/next-safe-action","tags":["javascript","next","nextjs","react","rsc","react server components","mutation","action","actions","typescript"],"install":[{"cmd":"npm install next-safe-action","lang":"bash","label":"npm"},{"cmd":"yarn add next-safe-action","lang":"bash","label":"yarn"},{"cmd":"pnpm add next-safe-action","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Required for Next.js Server Actions functionality.","package":"next","optional":false},{"reason":"Core React library for component usage and hooks.","package":"react","optional":false},{"reason":"DOM-specific renderers for React components.","package":"react-dom","optional":false}],"imports":[{"note":"next-safe-action is an ESM-first library. Use `import` statements. `createSafeActionClient` is used to initialize the action client with middleware and context.","wrong":"const { createSafeActionClient } = require('next-safe-action');","symbol":"createSafeActionClient","correct":"import { createSafeActionClient } from 'next-safe-action';"},{"note":"`useAction` is a React hook and must be imported from the `/hook` subpath. It is designed for client components.","wrong":"import { useAction } from 'next-safe-action';","symbol":"useAction","correct":"import { useAction } from 'next-safe-action/hook';"},{"note":"This is not a direct import but the result of calling `client.action()`. It represents the actual server action function.","symbol":"safeAction","correct":"const action = client.action(schema, async (input) => { /* ... */ });"}],"quickstart":{"code":"import { createSafeActionClient } from 'next-safe-action';\nimport { z } from 'zod';\nimport { useAction } from 'next-safe-action/hook';\n\n// server/actions.ts\nexport const actionClient = createSafeActionClient();\n\nexport const addTodo = actionClient\n  .schema(z.object({ text: z.string().min(1) }))\n  .action(async ({ text }) => {\n    // Simulate database operation\n    await new Promise(resolve => setTimeout(resolve, 500));\n    console.log(`Adding todo: ${text}`);\n    return { success: true, newTodo: { id: Date.now(), text } };\n  });\n\n// app/page.tsx (or any client component)\n'use client';\n\nimport { useState } from 'react';\nimport { addTodo } from '@/server/actions'; // Adjust path as needed\n\nexport default function TodoForm() {\n  const [todoText, setTodoText] = useState('');\n  const { execute, result, status } = useAction(addTodo, {\n    onSuccess: (data) => {\n      console.log('Todo added successfully:', data?.newTodo);\n      setTodoText('');\n    },\n    onError: (error) => {\n      console.error('Failed to add todo:', error);\n    }\n  });\n\n  const isLoading = status === 'executing';\n\n  return (\n    <form onSubmit={(e) => {\n      e.preventDefault();\n      execute({ text: todoText });\n    }}>\n      <input\n        type=\"text\"\n        value={todoText}\n        onChange={(e) => setTodoText(e.target.value)}\n        placeholder=\"New todo item\"\n        disabled={isLoading}\n      />\n      <button type=\"submit\" disabled={isLoading}>\n        {isLoading ? 'Adding...' : 'Add Todo'}\n      </button>\n      {result.validationErrors?.text && (\n        <p style={{ color: 'red' }}>{result.validationErrors.text[0]}</p>\n      )}\n      {result.serverError && (\n        <p style={{ color: 'red' }}>{result.serverError}</p>\n      )}\n    </form>\n  );\n}","lang":"typescript","description":"This quickstart demonstrates how to define a type-safe server action with input validation using Zod and then execute it from a client component using the `useAction` hook, handling loading states and displaying errors."},"warnings":[{"fix":"Consult the official v7 to v8 migration guide at `https://next-safe-action.dev/docs/migrations/v7-to-v8` to update action client initialization and action definitions.","message":"next-safe-action v8 introduced breaking changes, including how action clients are created and used. Refer to the migration guide for a smooth transition.","severity":"breaking","affected_versions":">=8.0.0 <8.5.0"},{"fix":"Ensure your client-side logic correctly handles the narrowed types. When `result.data` is present, `serverError` and `validationErrors` will be `undefined` (and vice-versa). This improves type safety but might require adjustments to conditional checks.","message":"Version 8.5.0 narrowed `SafeActionResult` into a discriminated union, meaning `data`, `serverError`, and `validationErrors` are now mutually exclusive in the type system.","severity":"gotcha","affected_versions":">=8.5.0"},{"fix":"Always use ES module `import` syntax (e.g., `import { createSafeActionClient } from 'next-safe-action';`). Ensure your Next.js project and `tsconfig.json` are configured for ESM.","message":"Using `require()` for imports will lead to errors as `next-safe-action` is an ESM-first package. Node.js environments configured for CommonJS will not resolve imports correctly.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Add the `'use client';` directive at the top of any file defining a component that calls `useAction`.","message":"When using `useAction`, ensure the component where it's called is a React Client Component. Server Actions are defined on the server, but the `useAction` hook must be run in a client environment.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Verify that `next-safe-action` is installed correctly (`npm i next-safe-action`) and ensure you are using ES module imports (`import ... from 'next-safe-action';`). If using CommonJS in Node.js, you might need to configure your project for ESM or use a bundler that handles ESM correctly.","cause":"Incorrect import path or CommonJS project attempting to import an ESM package.","error":"Error: Cannot find module 'next-safe-action' or its corresponding type declarations."},{"fix":"The `useAction` hook must be imported from the `/hook` subpath: `import { useAction } from 'next-safe-action/hook';`.","cause":"Incorrect import path for the `useAction` hook.","error":"Error: `useAction` is not a function or `useAction` is not defined."},{"fix":"Always conditionally access properties of `result` and its nested objects, e.g., `result?.validationErrors?.fieldName` or `if (result.validationErrors) { /* handle errors */ }`. With v8.5.0+, leverage the discriminated union by checking `result.validationErrors` first.","cause":"Accessing properties like `validationErrors` directly on `result` without checking if `result` or `result.validationErrors` is defined, or if the action even produced validation errors.","error":"TypeError: Cannot read properties of undefined (reading 'validationErrors')"}],"ecosystem":"npm"}