Skip to main content

forms

Use when building forms - covers React Hook Form, Zod validation, and form patterns

소스 정보

저장소
trycompai/comp
최근 소스 활동
2026년 4월 27일 13:56
감지된 SKILL.md 언어
영어
스타
2,074
포크
441

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
forms
description
Use when building forms - covers React Hook Form, Zod validation, and form patterns
Source Cursor rule: `.cursor/rules/forms.mdc`. Original Cursor alwaysApply: `false`. # Forms: React Hook Form + Zod **All forms MUST use React Hook Form with Zod validation.** ## Basic Pattern ```tsx import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; import { Button, Input } from '@trycompai/design-system'; // 1. Define schema const formSchema = z.object({ email: z.string().email('Invalid email'), password: z.string().min(8, 'Min 8 characters'), }); // 2. Infer type type FormData = z.infer<typeof formSchema>; // 3. Use in component function MyForm() { const { register, handleSubmit, formState: { errors, isSubmitting }, } = useForm<FormData>({ resolver: zodResolver(formSchema), }); return ( <form onSubmit={handleSubmit(onSubmit)}> <Input {...register('email')} /> {errors.email && <p>{errors.email.message}</p>} <Button type="submit" loading={isSubmitting}> Submit </Button> </form> ); } ``` ## Zod Schema Patterns ```tsx const profileSchema = z.object({ // Strings name: z.string().min(1, 'Required'), email: z.string().email(), website: z.string().url().optional(), // Numbers (coerce for inputs) age: z.coerce.number().int().min(0), price: z.coerce.number().positive(), // Arrays tags: z.array(z.string()).min(1), // Enums status: z.enum(['active', 'inactive']), }); // Cross-field validation const passwordSchema = z.object({ password: z.string().min(8), confirmPassword: z.string(), }).refine(d => d.password === d.confirmPassword, { message: "Passwords don't match", path: ['confirmPassword'], }); ``` ## Controller for Complex Components ```tsx import { Controller } from 'react-hook-form'; import { Select, SelectContent, SelectItem, SelectTrigger } from '@trycompai/design-system'; <Controller name="status" control={control} render={({ field }) => ( <Select onValueChange={field.onChange} value={field.value}> <SelectTrigger>{field.value || 'Select...'}</SelectTrigger> <SelectContent> <SelectItem value="active">Active</SelectItem> <SelectItem value="inactive">Inactive</SelectItem> </SelectContent> </Select> )} /> ``` ## Form State ```tsx const { register, handleSubmit, control, watch, // Watch field values setValue, // Set field programmatically reset, // Reset form setError, // Set error manually formState: { errors, // Field errors isSubmitting, // Submitting isValid, // All valid isDirty, // Modified }, } = useForm<FormData>({ resolver: zodResolver(schema), mode: 'onChange', // Validate on change }); ``` ## Error Handling ```tsx const onSubmit = async (data: FormData) => { try { await submitToApi(data); } catch (error) { // Field-specific error setError('email', { message: 'Email taken' }); // Or root error setError('root', { message: 'Something went wrong' }); } }; // Display root error {errors.root && <p>{errors.root.message}</p>} ``` ## Dynamic Fields ```tsx import { useFieldArray } from 'react-hook-form'; const { fields, append, remove } = useFieldArray({ control, name: 'items', }); {fields.map((field, index) => ( <div key={field.id}> <Input {...register(`items.${index}.name`)} /> <Button type="button" onClick={() => remove(index)}>Remove</Button> </div> ))} <Button type="button" onClick={() => append({ name: '' })}>Add</Button> ``` ## Anti-Patterns ```tsx // ❌ useState for form fields const [email, setEmail] = useState(''); // ❌ Manual validation if (email.length < 5) setError('Too short'); // ❌ Missing button type (defaults to submit) <Button onClick={handleCancel}>Cancel</Button> // ✅ Correct const { register } = useForm(); const schema = z.object({ email: z.string().min(5) }); <Button type="button" onClick={handleCancel}>Cancel</Button> ```
GitHub에서 보기