https://github.com/teamreflex/typed-action
Zod validated React server actions
https://github.com/teamreflex/typed-action
next react server-actions zod
Last synced: 5 months ago
JSON representation
Zod validated React server actions
- Host: GitHub
- URL: https://github.com/teamreflex/typed-action
- Owner: teamreflex
- License: mit
- Created: 2024-02-23T12:18:38.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-02-23T15:27:04.000Z (over 2 years ago)
- Last Synced: 2025-09-14T11:56:30.957Z (10 months ago)
- Topics: next, react, server-actions, zod
- Language: TypeScript
- Homepage:
- Size: 44.9 KB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# @teamreflex/typed-action
[](https://npm.im/@teamreflex/typed-action) [](https://npm.im/@teamreflex/typed-action)
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`.
## Install
```bash
npm i @teamreflex/typed-action
```
## Usage
Define a Zod schema for your form data:
```ts
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.
```ts
"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:
```tsx
import { updateUser } from "./actions"
function UpdateUserForm() {
return (
Update
)
}
```
## `typedAction` Options
### `form`: `FormData | Record`
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.
### `schema`: `ZodObject`
Any Zod schema.
### `onValidate`: `({ input: T }) => Promise`
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.
### `postValidate`: `(({ input: T, output: R }) => void) | undefined`
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`.
## Examples
| Link | Description |
| ----------------------------------------------------- | ---------------------------------------------------------------- |
| [01-useFormState](examples/01-useFormState) | Using React's `useFormState` hook to render success/error status |
| [02-nextjs-redirect](examples/02-nextjs-redirect) | Perform a redirect using Next's `redirect` helper |
| [03-custom-errors](examples/03-custom-errors) | Throw errors manually to seamlessly use the same state |
| [04-helper-components](examples/04-helper-components) | Examples of helper components to make errors easier to render |
| [05-useTransition](examples/05-useTransition) | Server actions don't always need to be forms |
## License
MIT © [Reflex](https://github.com/teamreflex)