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-action
Define 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