Cursor rule
.cursor/rules/forms.mdcForm handling patterns with React Hook Form and Zod validation
Cursor rules
Quality
73/100
Scores the file, not the repository.Length
676 words
12 headings · 6 code blocksRepository
63
— · pushed 15 days agoLast changed
3 days ago
First indexed 3 days ago.1234567# Form Handling Rules89## React Hook Form + Zod Pattern1011### Basic Form Setup12```tsx13// components/forms/UserForm.tsx14interface UserFormProps {15 onSubmit: (data: UserFormData) => void16 initialData?: Partial<User>17 isLoading?: boolean18}1920export const UserForm = ({ onSubmit, initialData, isLoading }: UserFormProps) => {21 const form = useForm<UserFormData>({22 resolver: zodResolver(userSchema),23 defaultValues: {24 name: initialData?.name || '',25 email: initialData?.email || '',26 phone: initialData?.phone || '',27 }28 })2930 return (31 <Form {...form}>32 <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">33 <FormField34 control={form.control}35 name="name"36 render={({ field }) => (37 <FormItem>38 <FormLabel>Full Name</FormLabel>39 <FormControl>40 <Input placeholder="Enter full name" {...field} />41 </FormControl>42 <FormMessage />43 </FormItem>44 )}45 />4647 <FormField48 control={form.control}49 name="email"50 render={({ field }) => (51 <FormItem>52 <FormLabel>Email</FormLabel>53 <FormControl>54 <Input type="email" placeholder="Enter email" {...field} />55 </FormControl>56 <FormMessage />57 </FormItem>58 )}59 />6061 <Button type="submit" disabled={isLoading} className="w-full">62 {isLoading ? 'Saving...' : 'Save User'}63 </Button>64 </form>65 </Form>66 )67}68```6970### Form Hook Pattern71```tsx72// hooks/useUserForm.ts73export const useUserForm = (userId?: string) => {74 const { data: user } = useUser(userId)75 const createUser = useCreateUser()76 const updateUser = useUpdateUser()7778 const form = useForm<UserFormData>({79 resolver: zodResolver(userSchema),80 defaultValues: {81 name: '',82 email: '',83 phone: '',84 }85 })8687 // Reset form when user data loads88 useEffect(() => {89 if (user) {90 form.reset({91 name: user.name,92 email: user.email,93 phone: user.phone || '',94 })95 }96 }, [user, form])9798 const handleSubmit = (data: UserFormData) => {99 if (userId) {100 updateUser.mutate({ id: userId, data })101 } else {102 createUser.mutate(data)103 }104 }105106 const isLoading = createUser.isPending || updateUser.isPending107108 return {109 form,110 handleSubmit,111 isLoading,112 reset: form.reset,113 }114}115```116117### Advanced Form Patterns118119#### Multi-Step Form120```tsx121// hooks/useMultiStepForm.ts122export const useMultiStepForm = <T extends Record<string, any>>(123 steps: Array<{ key: string; schema: ZodSchema<any> }>,124 onComplete: (data: T) => void125) => {126 const [currentStep, setCurrentStep] = useState(0)127 const [formData, setFormData] = useState<Partial<T>>({})128129 const currentStepConfig = steps[currentStep]130131 const form = useForm({132 resolver: zodResolver(currentStepConfig.schema),133 defaultValues: formData[currentStepConfig.key] || {}134 })135136 const nextStep = (data: any) => {137 setFormData(prev => ({ ...prev, [currentStepConfig.key]: data }))138139 if (currentStep < steps.length - 1) {140 setCurrentStep(prev => prev + 1)141 } else {142 onComplete({ ...formData, [currentStepConfig.key]: data } as T)143 }144 }145146 const prevStep = () => {147 if (currentStep > 0) {148 setCurrentStep(prev => prev - 1)149 }150 }151152 return {153 form,154 currentStep,155 totalSteps: steps.length,156 isFirstStep: currentStep === 0,157 isLastStep: currentStep === steps.length - 1,158 nextStep,159 prevStep,160 handleSubmit: form.handleSubmit(nextStep)161 }162}163```164165#### Dynamic Form Fields166```tsx167// components/forms/DynamicFieldArray.tsx168export const DynamicFieldArray = ({ name, control }: DynamicFieldArrayProps) => {169 const { fields, append, remove } = useFieldArray({170 control,171 name,172 })173174 return (175 <div className="space-y-4">176 {fields.map((field, index) => (177 <div key={field.id} className="flex gap-2 items-end">178 <FormField179 control={control}180 name={`${name}.${index}.value`}181 render={({ field }) => (182 <FormItem className="flex-1">183 <FormLabel>Item {index + 1}</FormLabel>184 <FormControl>185 <Input {...field} />186 </FormControl>187 <FormMessage />188 </FormItem>189 )}190 />191 <Button192 type="button"193 variant="outline"194 size="icon"195 onClick={() => remove(index)}196 >197 <X className="h-4 w-4" />198 </Button>199 </div>200 ))}201202 <Button203 type="button"204 variant="outline"205 onClick={() => append({ value: '' })}206 >207 <Plus className="h-4 w-4 mr-2" />208 Add Item209 </Button>210 </div>211 )212}213```214215### Form Validation Rules216217#### Complex Validation Schema218```tsx219// lib/validations/userProfile.ts220export const userProfileSchema = z.object({221 personal: z.object({222 firstName: z.string().min(2, 'First name required'),223 lastName: z.string().min(2, 'Last name required'),224 email: emailSchema,225 phone: phoneSchema.optional(),226 dateOfBirth: z.string().optional(),227 }),228 address: z.object({229 street: z.string().min(5, 'Street address required'),230 city: z.string().min(2, 'City required'),231 state: z.string().min(2, 'State required'),232 zipCode: z.string().regex(/^\d{5}(-\d{4})?$/, 'Invalid zip code'),233 country: z.string().min(2, 'Country required'),234 }),235 preferences: z.object({236 newsletter: z.boolean().default(false),237 notifications: z.boolean().default(true),238 theme: z.enum(['light', 'dark', 'system']).default('system'),239 })240})241```242243#### Conditional Validation244```tsx245export const conditionalSchema = z.object({246 userType: z.enum(['individual', 'business']),247 email: emailSchema,248 companyName: z.string().optional(),249 taxId: z.string().optional(),250}).refine(251 (data) => {252 if (data.userType === 'business') {253 return data.companyName && data.taxId254 }255 return true256 },257 {258 message: "Company name and tax ID required for business accounts",259 path: ["companyName"],260 }261)262```263264## Form Anti-Patterns265- Manual form state management266- Inline validation logic267- No error handling268- Uncontrolled components mixing with controlled269- No loading states during submission270271## Form Best Practices272- Always use React Hook Form + Zod273- Extract form logic to custom hooks274- Provide loading states during submission275- Reset forms after successful submission276- Handle both client and server validation errors277
Also in chihebnabil/lovable-boilerplate
Diff this repo’s formatsOne repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| chihebnabil/lovable-boilerplate.cursor/rules/design.mdc · 63 | Cursor rules | styleuido-not | 65/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/hooks.mdc · 63 | Cursor rules | styleuido-not | 61/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/services.mdc · 63 | Cursor rules | archtypesdo-not | 73/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/typescript.mdc · 63 | Cursor rules | setupstyletypessecurity+2 | 73/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/architecture.instructions.md · 63 | Copilot instructions | stylearchtypesdatabase+2 | 69/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/design.instructions.md · 63 | Copilot instructions | lint-formatstyleuido-not | 61/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/development.instructions.md · 63 | Copilot instructions | setupbuildtestlint-format+7 | 88/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/hooks.instructions.md · 63 | Copilot instructions | styleui | 54/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/lib.instructions.md · 63 | Copilot instructions | archtypesdo-not | 69/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/pages.instructions.md · 63 | Copilot instructions | archuido-not | 69/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/quality.instructions.md · 63 | Copilot instructions | lint-formatdeploymentdo-not | 63/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/reusable.instructions.md · 63 | Copilot instructions | uido-notagent-behaviour | 32/100 | 3 days ago | |
| chihebnabil/lovable-boilerplateCLAUDE.md · 63 | CLAUDE.md | setupbuildtestlint-format+5 | 89/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/core.mdc · 63 | Cursor rules | buildlint-formatarchui+1 | 92/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/components.instructions.md · 63 | Copilot instructions | styleuido-not | 61/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.github/instructions/global.instructions.md · 63 | Copilot instructions | buildlint-formatstylearch+4 | 100/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/components.mdc · 63 | Cursor rules | stylearchuido-not | 65/100 | 3 days ago | |
| chihebnabil/lovable-boilerplate.cursor/rules/quality.mdc · 63 | Cursor rules | buildlint-formatstyleui+3 | 88/100 | 3 days ago |
Diff against .cursor/rules/design.mdc Diff against .cursor/rules/hooks.mdc Diff against .cursor/rules/services.mdc Diff against .cursor/rules/typescript.mdc Diff against .github/instructions/architecture.instructions.md Diff against .github/instructions/design.instructions.md Diff against .github/instructions/development.instructions.md Diff against .github/instructions/hooks.instructions.md Diff against .github/instructions/lib.instructions.md Diff against .github/instructions/pages.instructions.md Diff against .github/instructions/quality.instructions.md Diff against .github/instructions/reusable.instructions.md Diff against CLAUDE.md Diff against .cursor/rules/core.mdc Diff against .github/instructions/components.instructions.md Diff against .github/instructions/global.instructions.md Diff against .cursor/rules/components.mdc Diff against .cursor/rules/quality.mdc
Similar configs
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| hiromaily/go-crypto-wallet.cursor/rules/typescript.mdc · 126 | Cursor rules | setupbuildtestlint-format+6 | 100/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/10-hermes-api.mdc · 45 | Cursor rules | testlint-formatstylearch+5 | 100/100 | 3 days ago | |
| markstev/mark-starter.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+6 | 99/100 | 3 days ago | |
| Allymahmoud/case-intake-platform.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| dodgecfr/combatfilms-webapp.cursor/rules/frontend.mdc · 0 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| deifos/clipmira-subtitles.cursor/rules/frontend.mdc · 1 | Cursor rules | setuptestlint-formatstyle+7 | 99/100 | 3 days ago | |
| langflow-ai/langflow.cursor/rules/docs_development.mdc · 153k | Cursor rules | setupbuildtestlint-format+7 | 97/100 | 3 days ago | |
| TechSquidTV/Hermes.cursor/rules/20-hermes-api-tests.mdc · 45 | Cursor rules | teststyletesting-strategysecurity+3 | 97/100 | 3 days ago |
