The form writes itself
formstand-gen turns a zod schema or a TypeScript type into a working component: typed initial values, a bound control per field, array sections, accessible markup. One command, no runtime dependency.
See what it generates
Write the schema once. It gives you the types, the validation, the typed paths, and with formstand-cli, the component itself.
Point the generator at a schema you already have:
npx formstand-gen src/profileSchema.ts --ui mui --sections panel --columns 2 --out src/ProfileForm.tsxIt 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.
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