Skip to content

formstandZod-schema-first forms for React

Write the schema once. It gives you the types, the validation, the typed paths, and with formstand-cli, the component itself.

A form with a green check, resting on a music stand

Schema in, form out ​

Point the generator at a schema you already have:

bash
npx formstand-gen src/profileSchema.ts --ui mui --sections panel --columns 2 --out src/ProfileForm.tsx

It reads the schema with your own copy of zod, so the output matches what the schema actually says. Optional fields, nullable fields, enum options, defaults, and nested arrays all come through. What you get is a plain .tsx file with no markers and no regeneration magic: edit it, move it, delete the CLI. Nothing in the generated code imports from formstand-cli.

For anything bigger than a handful of fields, --layout module writes a feature folder instead of one file, with the schema, the pre-wired hooks, one file per field, and one per section.

Read the CLI docs or build a form in the browser, where the same generator runs client-side.

Or write it by hand ​

tsx
const schema = z.object({ email: z.string().email(), age: z.number().min(18) });

export const SignupForm = () => {
  const form = useForm(schema, { initialValues: { email: "", age: 18 } });
  const email = useField(form, "email");

  return (
    <form onSubmit={form.handleSubmit(save)}>
      <input {...textInputProps(email)} />
      <span role="alert">{email.error?.[0]}</span>
    </form>
  );
};

No resolver to configure, no register, no coercion layer. Dirtiness is derived by comparing values against initial values rather than tracked, so it cannot drift, and form.diff() gives you a PATCH-ready payload of exactly what changed.

Getting started · Typed paths · Migrating from react-hook-form

Built on zod and zustand. Released under the MIT License.