Skills

Install

$ npx ai-agents-skills add --skill form-validation
Domain v1.1

Form Validation

Form/schema validation across Zod, Yup, React Hook Form, Formik. Context-aware.

When to Use

  • Validating user input (forms, API requests)
  • Runtime schema validation
  • Parsing external data with type safety
  • Ensuring data conforms to types
  • Managing form state with validation

Don’t use when:

  • Compile-time typing (typescript skill)
  • Simple validation (<3 fields)

Critical Patterns

✅ REQUIRED [CRITICAL]: Check Context First

ALWAYS check what the project uses before recommending a library:

// Step 1: Check AGENTS.md for installed skills
// Step 2: Check package.json for installed libraries
// Step 3: Use existing library, DON'T force new ones

// ✅ CORRECT: Context-aware
// If project has Zod → use Zod patterns
// If project has Yup → use Yup patterns
// If project has React Hook Form → use RHF patterns
// If project has Formik → use Formik patterns

// ❌ WRONG: Force new lib
// "Add Zod" when project uses Yup
// "Use RHF" when project uses Formik

✅ REQUIRED: Infer Types from Schemas

// ✅ CORRECT: Schema as single source of truth
// Zod
const schema = z.object({ name: z.string() });
type User = z.infer<typeof schema>;

// Yup
const schema = yup.object({ name: yup.string().required() });
type User = yup.InferType<typeof schema>;

// ❌ WRONG: Separate interface (drifts)
interface User { name: string }
const schema = z.object({ name: z.string() });

✅ REQUIRED: Safe Error Handling

// ✅ CORRECT: Non-throwing validation
// Zod
const result = schema.safeParse(data);
if (result.success) {
  use(result.data);
} else {
  handle(result.error);
}

// Yup
try {
  const valid = await schema.validate(data);
} catch (error) {
  if (error instanceof yup.ValidationError) {
    handle(error);
  }
}

// ❌ WRONG: Unhandled throws
const data = schema.parse(input); // May throw

✅ REQUIRED: Chain Validations

// ✅ CORRECT: Multiple constraints
const email = z.string().email().min(5).max(100);
const email = yup.string().email().required().max(100);

// ❌ WRONG: Single validation
const email = z.string(); // Too permissive

Decision Tree

Project has validation library?
  → Yes: Check AGENTS.md or package.json
    → Zod installed? → See references/zod.md
    → Yup installed? → See references/yup.md
    → React Hook Form? → See references/react-hook-form.md
    → Formik? → See references/formik.md
  → No: Recommend modern default (React Hook Form + Zod for React, Zod for Node.js)

Schema validation only (no forms)?
  → Use Zod or Yup (see references for library-specific patterns)

Form management needed (React)?
  → React Hook Form (modern, performant) or Formik (legacy support)
  → See references/react-hook-form.md or references/formik.md

Async validation required?
  → All libraries support it:
    - Zod: .parseAsync() or .refine() with async
    - Yup: .validate() is async
    - React Hook Form: resolver or custom async rules
    - Formik: validate prop with async function

Complex nested structures?
  → Zod: z.object() nesting
  → Yup: yup.object() nesting
  → React Hook Form: dot notation (user.address.street)
  → Formik: FieldArray or dot notation

Example

Context-aware validation setup:

// Check AGENTS.md first:
// "Skills: react, typescript, form-validation"
//
// Check package.json:
// "zod": "^3.22.0",
// "react-hook-form": "^7.48.0"
//
// → Use React Hook Form + Zod

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  email: z.string().email('Invalid email').min(1, 'Required'),
  age: z.number().int().min(18, 'Must be 18+'),
});

type FormData = z.infer<typeof schema>;

function MyForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <input {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="number" {...register('age', { valueAsNumber: true })} />
      {errors.age && <span>{errors.age.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

Edge Cases

Multiple libs: Use appropriate for context (Zod for API, Yup for legacy Formik).

No library: Recommend RHF + Zod (React) or Zod (Node.js).

Migration: One form/endpoint at a time. Don’t mix within same form.

TS strict: All libs need strict: true in tsconfig.


Resources

ReferenceWhen to Read
references/zod.mdUsing Zod for schema validation
references/yup.mdUsing Yup for validation
references/react-hook-form.mdUsing React Hook Form for forms
references/formik.mdUsing Formik for React forms

See references/README.md for complete navigation.