{"id":44754365,"url":"https://github.com/teamreflex/typed-action","last_synced_at":"2026-02-15T23:35:42.030Z","repository":{"id":224062268,"uuid":"762263358","full_name":"teamreflex/typed-action","owner":"teamreflex","description":"Zod validated React server actions","archived":false,"fork":false,"pushed_at":"2024-02-23T15:27:04.000Z","size":46,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-14T11:56:30.957Z","etag":null,"topics":["next","react","server-actions","zod"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/teamreflex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-23T12:18:38.000Z","updated_at":"2024-08-06T10:49:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"d7eaf7ae-777c-46e4-b061-110e5126cf98","html_url":"https://github.com/teamreflex/typed-action","commit_stats":null,"previous_names":["teamreflex/typed-action"],"tags_count":3,"template":false,"template_full_name":"egoist/ts-lib-starter","purl":"pkg:github/teamreflex/typed-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamreflex%2Ftyped-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamreflex%2Ftyped-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamreflex%2Ftyped-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamreflex%2Ftyped-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teamreflex","download_url":"https://codeload.github.com/teamreflex/typed-action/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teamreflex%2Ftyped-action/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29492776,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T23:34:01.544Z","status":"ssl_error","status_checked_at":"2026-02-15T23:33:27.984Z","response_time":118,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["next","react","server-actions","zod"],"created_at":"2026-02-15T23:35:37.101Z","updated_at":"2026-02-15T23:35:42.025Z","avatar_url":"https://github.com/teamreflex.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @teamreflex/typed-action\n\n[![npm version](https://badgen.net/npm/v/@teamreflex/typed-action)](https://npm.im/@teamreflex/typed-action) [![npm downloads](https://badgen.net/npm/dm/@teamreflex/typed-action)](https://npm.im/@teamreflex/typed-action)\n\nConvenience wrapper for Zod validation in React server actions.\n\nRationale: 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`.\n\n## Install\n\n```bash\nnpm i @teamreflex/typed-action\n```\n\n## Usage\n\nDefine a Zod schema for your form data:\n\n```ts\nimport { z } from \"zod\"\n\nconst updateUserSchema = z.object({\n  name: z.string().min(3).max(64),\n  email: z.string().email(),\n})\n```\n\nDefine a new action. This can be done as a const or function, if you wanted to mutate the form data before validation.\n\n```ts\n\"use server\"\nimport { typedAction } from \"@teamreflex/typed-action\"\n\nexport const updateUser = async (form: FormData) =\u003e\n  typedAction({\n    form,\n    schema: updateUserSchema,\n    onValidate: async ({ input }) =\u003e {\n      //                 ^? { name: string, email: string }\n      return await db.update(users).set(input).where({ id: 1 })\n    },\n  })\n```\n\nThen use it in your React components:\n\n```tsx\nimport { updateUser } from \"./actions\"\n\nfunction UpdateUserForm() {\n  return (\n    \u003cform action={updateUser} className=\"flex flex-col gap-2\"\u003e\n      \u003cinput type=\"text\" name=\"name\" /\u003e\n      \u003cinput type=\"email\" name=\"email\" /\u003e\n      \u003cbutton type=\"submit\"\u003eUpdate\u003c/button\u003e\n    \u003c/form\u003e\n  )\n}\n```\n\n## `typedAction` Options\n\n### `form`: `FormData | Record\u003cstring, unknown\u003e`\n\nCan 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.\n\n### `schema`: `ZodObject`\n\nAny Zod schema.\n\n### `onValidate`: `({ input: T }) =\u003e Promise\u003cR\u003e`\n\nAn 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.\n\n### `postValidate`: `(({ input: T, output: R }) =\u003e void) | undefined`\n\nAn 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.\n\n`T` is the Zod validation output/input to `onValidate`, and `R` is the output of `onValidate`.\n\n## Examples\n\n| Link                                                  | Description                                                      |\n| ----------------------------------------------------- | ---------------------------------------------------------------- |\n| [01-useFormState](examples/01-useFormState)           | Using React's `useFormState` hook to render success/error status |\n| [02-nextjs-redirect](examples/02-nextjs-redirect)     | Perform a redirect using Next's `redirect` helper                |\n| [03-custom-errors](examples/03-custom-errors)         | Throw errors manually to seamlessly use the same state           |\n| [04-helper-components](examples/04-helper-components) | Examples of helper components to make errors easier to render    |\n| [05-useTransition](examples/05-useTransition)         | Server actions don't always need to be forms                     |\n\n## License\n\nMIT \u0026copy; [Reflex](https://github.com/teamreflex)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteamreflex%2Ftyped-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteamreflex%2Ftyped-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteamreflex%2Ftyped-action/lists"}