Write and refactor React forms using react-hook-form with Zod validation. Use when creating new form components, converting existing forms to react-hook-form, or implementing form validation patterns.
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Write and refactor React forms using react-hook-form with Zod validation. Use when creating new form components, converting existing forms to react-hook-form, or implementing form validation patterns.
React Hook Form Writer
This skill helps you write new forms and refactor existing forms to use react-hook-form following project best practices.
When to Use
Creating new form components from scratch
Converting existing forms to react-hook-form
Adding validation to forms
Implementing complex form patterns (nested forms, field arrays, multi-step)
Core Principles
1. Always Use Zod for Validation
Define schemas with Zod and integrate via zodResolver:
import { z } from"zod";
import { zodResolver } from"@hookform/resolvers/zod";
const formSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
age: z.number().min(18, "Must be at least 18"),
});
typeFormValues = z.infer<typeof formSchema>;
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
email: "",
age: 18,
},
});
2. Prefer useController Over Controller
Use useController hook for better composability in custom field components:
Leverage react-hook-form's uncontrolled approach for native inputs:
// Good: Uncontrolled with register
<input {...register("name")} />
// Only use Controller/useController for third-party controlled components// (e.g., shadcn Select, custom date pickers, rich text editors)
4. Use field.onChange for User Interactions, setValue for Programmatic Updates
When working with useController, use field.onChange for user interactions:
// Good: field.onChange for user interactionsconst { field } = useController({ name: "status", control });
<SelectonValueChange={field.onChange}value={field.value}>
{options.map((opt) => (
<SelectItemkey={opt.value}value={opt.value}>
{opt.label}
</SelectItem>
))}
</Select>// Bad: setValue for user interactions (breaks controller lifecycle)<SelectonValueChange={(v) => setValue("status", v)} value={watch("status")}>
Use setValue (from useFormContext) for programmatic updates in effects:
CRITICAL: Never use field.onChange inside useEffect dependencies
useController returns new field objects on every render. Including them in useEffect dependencies while also calling field.onChange() inside the effect causes infinite loops:
// BAD: Infinite loop - field objects change every renderconst { field } = useController({ name: "status" });
useEffect(() => {
field.onChange(defaultValue); // Triggers re-render
}, [field, defaultValue]); // field changes → effect runs → onChange → re-render → repeat// GOOD: Use setValue (stable) for programmatic updates in effectsconst { setValue } = useFormContext();
const { field } = useController({ name: "status" });
useEffect(() => {
setValue("status", defaultValue); // setValue is stable
}, [defaultValue, setValue]);
// User interactions still use field.onChangeconsthandleSelect = (value: string) => {
field.onChange(value);
};
Summary:
field.onChange → user interaction handlers (onClick, onSelect, etc.)
setValue → programmatic updates in useEffect or callbacks based on external data
5. Always Provide Default Values
Always provide defaultValues in useForm for all fields: