Skip to main content
Version: Next

Getting Started

next-server-actions is a utility package for Next.js designed to simplify and enhance your experience working with server actions. It provides a collection of tools that help you build cleaner, more maintainable applications by extending the native Next.js and React APIs.

caution

next-server-actions requires Next.js 15 or higher with the App Router.


๐Ÿ“ฆ Installationโ€‹

npm install next-server-actions zod

๐Ÿ“ Project Structureโ€‹

A typical file structure using next-server-actions:

my-app
โ”œโ”€โ”€ app
โ”‚ โ”œโ”€โ”€ _components
โ”‚ โ”‚ โ””โ”€โ”€ sign-in.form.tsx # The form component
โ”‚ โ”œโ”€โ”€ _lib
โ”‚ โ”‚ โ””โ”€โ”€ actions
โ”‚ โ”‚ โ””โ”€โ”€ sign-in.ts # Server action logic
โ”‚ โ”‚ โ””โ”€โ”€ utils
โ”‚ โ”‚ โ””โ”€โ”€ server-actions.ts # Utility to create server actions
โ”‚ โ”œโ”€โ”€ layout.tsx
โ”‚ โ””โ”€โ”€ page.tsx
โ”œโ”€โ”€ next.config.ts
โ”œโ”€โ”€ package.json
...

๐Ÿงช Basic Usageโ€‹

1. Create a reusable server action clientโ€‹

This creates a createServerAction() function that can be reused for all your form actions.

app/_lib/utils/server-actions.ts
import { createClient } from "next-server-actions";

export const createServerAction = createClient({
// Optional: add middleware here (e.g. auth, logging, etc.)
});

2. Define a Zod schema and server actionโ€‹

This validates the form on the server using the schema before executing any logic.

app/_lib/actions/sign-in.ts
"use server";

import { createServerAction } from "../utils/server-actions";
import { z } from "zod";

const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});

export const signIn = createServerAction(schema, async (values) => {
// Your login logic here
return { ok: true };
});

3. Create a form component using useActionStateโ€‹

This uses useActionState to bind your form to the server action.

app/_components/sign-in.form.tsx
"use client";

import Form from "next/form";
import { useActionState } from "react";
import { signIn } from "../_lib/actions/sign-in";

export function SignInForm() {
const [state, action, pending] = useActionState(signIn, {
ok: false,
});

return (
<Form action={action}>
<label htmlFor="email">Email</label>
<input type="email" id="email" name="email" required />

<br />

<label htmlFor="password">Password</label>
<input type="password" id="password" name="password" required />

<hr />

<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
</Form>
);
}