Convenience wrapper for Zod validation in React server actions.
Rationale: I wasn't happy with how existing solutions revolved around using hooks. I wanted a solution that worked with useTransition (arbitrary objects) for button triggers, but also worked with forms (FormData) via useFormState and useFormStatus.
npm i @teamreflex/typed-actionDefine a Zod schema for your form data:
import { z } from "zod"
const updateUserSchema = z.object({
name: z.string().min(3).max(64),
email: z.string().email(),
})Define a new action. This can be done as a const or function, if you wanted to mutate the form data before validation.
"use server"
import { typedAction } from "@teamreflex/typed-action"
export const updateUser = async (form: FormData) =>
typedAction({
form,
schema: updateUserSchema,
onValidate: async ({ input }) => {
// ^? { name: string, email: string }
return await db.update(users).set(input).where({ id: 1 })
},
})Then use it in your React components:
import { updateUser } from "./actions"
function UpdateUserForm() {
return (
<form action={updateUser} className="flex flex-col gap-2">
<input type="text" name="name" />
<input type="email" name="email" />
<button type="submit">Update</button>
</form>
)
}Can be either a FormData or string-keyed object/Record. Objects allow for usage with useTransition usage of server actions, whereas FormData is more convenient for form submissions and required for useFormState usage.
Any Zod schema.
An async function that executes upon a successful Zod validation. The input type T is inferred from the schema, and the return type R is inferred from the return type of the function.
An optional function that executes after the onValidate function. Because Nextjs's implementation of redirect and notFound results in throws, these can't be done in onValidate as they get caught. Instead, you can use postValidate to handle these cases.
T is the Zod validation output/input to onValidate, and R is the output of onValidate.
| Link | Description |
|---|---|
| 01-useFormState | Using React's useFormState hook to render success/error status |
| 02-nextjs-redirect | Perform a redirect using Next's redirect helper |
| 03-custom-errors | Throw errors manually to seamlessly use the same state |
| 04-helper-components | Examples of helper components to make errors easier to render |
| 05-useTransition | Server actions don't always need to be forms |
MIT © Reflex