The recurring pain: the zod schema, the TypeScript type, and the form field names would drift apart, so you'd validate a field the form didn't have, or submit a shape the API rejected. Three sources of truth for one form.
I wrote a prompt that makes zod the single source of truth, infers the TS type from it, and wires react-hook-form so a typo in a field name is a compile error. The submit handler receives a fully parsed, typed value. No more drift.
Bonus question: how are you all handling async field validation (like username-taken) inside this without breaking the typing?
Build a typed form in React using react-hook-form and zod, with ONE source of truth for the shape.
RULES:
- The zod schema is the single source of truth. Derive the TS type with `z.infer`. Do NOT hand-write a separate interface for the form values.
- Wire zodResolver so validation and types come from the same schema.
- Register fields with a typed name so a misspelled field name is a COMPILE error, not a runtime surprise. No stringly-typed field names that TS can't check.
- The onSubmit handler receives the parsed, typed output of the schema (post-transform), not the raw input.
- Show inline, accessible error messages: each input is aria-describedby its error, invalid inputs get aria-invalid, and focus moves to the first error on failed submit.
- Handle: required fields, cross-field validation (e.g. confirmPassword matches password via .refine), and a submit that can itself fail (server error) mapped back onto the form.
STRICT TS, no any, no casting the resolver. Deliver the schema, the form component, and show that renaming a field in the schema without updating the JSX produces a type error.