name: form
description: Generate a type-safe form from a zod schema using react-hook-form, @hookform/resolvers, and Tamagui field components. Use when building any user input form — login, registration, payments, profile edits.
argument-hint: ""
Generate a validated form for $ARGUMENTS.
Steps
Check existing components — call
get_componentsand look for existing form field primitives before creating new onesDefine the zod schema — write a
z.object()schema in a dedicated<feature>.schema.tsfile; derive the TypeScript type withz.inferWire the form — use
useForm<FormValues>withzodResolver(schema)and explicitdefaultValuesfor every fieldRender with Controller — always use
Controller(neverregister) for React Native compatibility; use project form field components or TamaguiInput+Labelas fallbackHandle submission —
handleSubmit(onSubmit)whereonSubmitreceives the fully-typed payload; usesetError("root", ...)for server-returned errorsi18n — every label, placeholder, and error string wrapped with
Trans/t`…`Type-check — run
tsc --noEmitand fix all errors before reporting done
Canonical output shape
// <feature>.schema.ts
import { z } from "zod";
export const mySchema = z.object({ ... });
export type MyFormValues = z.infer<typeof mySchema>;
// <Feature>Form.tsx
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, Controller } from "react-hook-form";
import { mySchema, type MyFormValues } from "./<feature>.schema";
export function MyForm({ onSuccess }: { onSuccess: () => void }) {
const { t } = useLingui();
const { control, handleSubmit, formState: { errors, isSubmitting } } = useForm<MyFormValues>({
resolver: zodResolver(mySchema),
defaultValues: { ... },
});
const onSubmit = async (data: MyFormValues) => { ... };
return (
<YStack gap="$md">
<Controller
control={control}
name="fieldName"
render={({ field }) => (
<FormField label={t`Label`} error={errors.fieldName} {...field} />
)}
/>
<Button onPress={handleSubmit(onSubmit)} disabled={isSubmitting}>
<Trans>Submit</Trans>
</Button>
</YStack>
);
}
Rules
- Schema in a separate file when reused across screens
defaultValuesmust cover every field — no uncontrolled → controlled warnings- Never use
.optional()to silence TS errors — fix the type at the source - Never suppress
react-hooks/exhaustive-deps— fix the dependency - Run
tsc --noEmitafter generation; zero errors before reporting done