next-safe-form
v1.1.7
Published
Simplify React 19 Server Actions forms in Next.js
Maintainers
Readme
🚀 next-safe-form
The easiest way to handle forms with React 19 Server Actions in Next.js — clean, safe, and scalable.
✨ Why this library?
Using useActionState with forms in React 19 + Next.js can quickly become:
- repetitive
- inconsistent
- hard to scale
You often have to manually handle:
- loading states
- field validation errors
- server errors
- form state syncing
- side effects (toast, redirect, etc.)
👉 next-safe-form eliminates all this boilerplate.
⚡ Features
✅ Clean abstraction over
useActionState✅ Built for Next.js Server Actions
✅ Automatic handling of:
- loading state
- success state
- field errors
- server errors
✅ Type-safe
✅ Minimal and intuitive API
✅ Scalable architecture (client + server)
📦 Installation
npm install next-safe-formor
pnpm add next-safe-formor
yarn add next-safe-formor
bun add next-safe-form🧠 Core Idea
Instead of writing this over and over:
useActionState- error parsing
- state handling
- side effects
You just write:
useSafeActionForm();🚀 Usage
1️⃣ Server Side (Recommended)
Use createSafeAction to standardize your server logic.
"use server";
import { createSafeAction } from "next-safe-form";
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
password: z.string().min(6),
});
export const loginAction = createSafeAction({
schema,
handler: async (data) => {
// business logic
if (data.email !== "[email protected]") {
throw new Error("Invalid credentials");
}
return {
email: "",
password: "",
};
},
});2️⃣ Client Side
"use client";
import { useSafeActionForm } from "next-safe-form";
export default function LoginForm() {
const { formAction, isPending, fieldsErrors, serverError } = useSafeActionForm({
action: loginAction,
initialValues: {
email: "",
password: "",
},
onSuccess: () => {
alert("Login successful!");
},
onError: (err) => {
alert(err);
},
});
return (
<form action={formAction}>
<input name="email" placeholder="Email" />
{fieldsErrors?.email && <p>{fieldsErrors.email}</p>}
<input name="password" type="password" placeholder="Password" />
{serverError && <p>{serverError}</p>}
<button disabled={isPending}>{isPending ? "Loading..." : "Login"}</button>
</form>
);
}🧩 API
useSafeActionForm(options)
| Option | Type | Description |
| --------------- | -------- | ---------------------- |
| action | function | Server action |
| initialValues | object | Initial form values |
| onSuccess | function | Called on success |
| onError | function | Called on server error |
🔁 Returned values
| Property | Description |
| -------------- | ----------------------- |
| formAction | Form action handler |
| isPending | Loading state |
| fieldsErrors | Field validation errors |
| serverError | Server error message |
| data | Returned data |
| isSuccess | Success state |
| reset | Reset form |
createSafeAction(options)
| Option | Type | Description |
| --------- | --------------------- | ---------------------- |
| schema | Zod schema (optional) | Validation schema |
| handler | function | Business logic handler |
🧠 Error Handling Model
All actions follow a standardized format:
{
success: boolean,
data: T,
error?: {
type: "fields" | "server",
message?: string,
fields?: Record<string, string>
}
}🏗️ Roadmap
- [ ] Zod auto-inference (types)
- [ ] Client-side validation
- [ ] Form helpers (input binding)
- [ ] Devtools
- [ ] Toast integration
🤝 Contributing
Contributions are welcome!
📄 License
MIT
👤 Author
Built with ❤️ for Next.js developers by 4develhoper
