Forms
Checkout (complex — canonical)
// features/checkout/hooks/useCheckoutForm.ts
const form = useForm<CheckoutFormData>({
resolver: zodResolver(checkoutFormSchema),
mode: 'onBlur',
defaultValues: { ... },
});
Schema in utils/validation.ts with .refine() for conditional rules (recipient mode, address type).
Auth Forms (simple)
AuthForm.tsx uses controlled useState — not RHF. Acceptable for simple login.
Phone Validation
// utils/phone-validation.ts + libphonenumber-js
validatePhone(value, countryCode)
Component: components/ui/phone-input-shadcn.tsx
shadcn Form
<Form {...form}>
<FormField control={form.control} name="email" render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl><Input {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
</Form>
Conditional Zod
.refine(data => data.isRecipient || data.senderName, {
message: 'Sender name required',
path: ['senderName'],
})
Notifications
useNotification().notify() — toast on desktop, drawer on mobile.
providers/NotificationProvider.tsx
File Inputs in Forms
Not in RHF — separate useState<File | null> with preview. Pass File to service on submit.
Prefill from Auth
useEffect(() => {
if (user) form.reset({ email: user.email, phone: user.phone, ... });
}, [user]);