--- name: zod-react-hook-form description: Form validation combining Zod schemas with React Hook Form, including localized error messages, Server Action integration, and shadcn/ui Form components. Use when building forms, validating user input, handling form submissions, or implementing Server Actions with validation. --- # Zod + React Hook Form ## Schema Definition ```tsx // lib/validations.ts import { z } from 'zod'; export const contactFormSchema = z.object({ name: z.string().min(2, 'Name must be at least 2 characters'), email: z.string().email('Please enter a valid email'), phone: z.string().optional(), message: z.string().min(10, 'Message must be at least 10 characters'), }); export type ContactFormData = z.infer; export const bookingRequestSchema = z.object({ name: z.string().min(2), email: z.string().email(), phone: z.string().optional(), goals: z.string().min(10), experienceLevel: z.enum(['beginner', 'intermediate', 'advanced']), injuries: z.string().optional(), preferredTimes: z.string().min(5), sessionType: z.enum(['in-person', 'online']), }); export type BookingRequestData = z.infer; ``` ## Client Component Form ```tsx 'use client'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { useTranslations } from 'next-intl'; import { contactFormSchema, type ContactFormData } from '@/lib/validations'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; export function ContactForm() { const t = useTranslations('form'); const form = useForm({ resolver: zodResolver(contactFormSchema), defaultValues: { name: '', email: '', phone: '', message: '', }, }); const onSubmit = async (data: ContactFormData) => { try { const response = await fetch('/api/contact', { method: 'POST', body: JSON.stringify(data), }); if (!response.ok) throw new Error(); form.reset(); // Show success toast } catch { // Show error toast } }; return (
( {t('name')} )} /> ( {t('email')} )} /> ( {t('message')}