Skip to content
cccskills
FreePublished on GitHub

forms

Build React forms with React Hook Form and Zod. The skill shows how to define validation schemas, connect fields, display errors, handle submission state, and manage complex or dynamic inputs.

OriginalShow the original description

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

trycompai/comp2,058 stars(GitHub 2,058 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Building a validated sign-up or profile form
  • Adding cross-field checks such as password confirmation
  • Handling selectable or dynamically repeated fields

Files included (1)

  • SKILL.md4.0 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

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

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

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

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

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

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

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

// ❌ 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>

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Guide API endpoint changes so request DTOs, runtime validation, OpenAPI documentation, and MCP tool metadata stay aligned. It covers discoverability, authentication, uploads, long-running tasks, and response types that MCP tools cannot handle.

  • Adding an API endpoint with a request body
  • Updating DTO validation and OpenAPI metadata
  • Making an endpoint available as an MCP tool
trycompai/comp2.1KUpdated Oct 24, 2026

Review specified files for design system usage, replace legacy @trycompai/ui components and lucide-react icons with available @trycompai/design-system equivalents, and run the app type check to verify the migration.

  • Migrating legacy UI components
  • Replacing lucide-react icons with Carbon icons
  • Checking specified files for design system compliance
trycompai/comp2.1KUpdated Oct 24, 2026

audit-hooks

FreeSummary

Review specified files for outdated hook and API patterns, then replace them with the prescribed SWR hooks, API clients, or server API calls. The guidance covers data fetching, mutations, cache updates, and response handling, followed by a typecheck command.

  • Auditing files for disallowed API and hook patterns
  • Replacing raw fetch calls with the API client
  • Migrating server actions and database mutations to API hooks
trycompai/comp2.1KUpdated Oct 24, 2026

audit-rbac

FreeSummary

Review NestJS API endpoints and frontend components for role-based access control and audit log compliance. The skill lists permission, guard, route, and UI gating rules, then directs the agent to fix violations and run type checks.

  • Auditing API routes for permission decorators
  • Checking UI actions against user permissions
  • Fixing access control inconsistencies
trycompai/comp2.1KUpdated Oct 24, 2026

audit-tests

FreeSummary

Audit unit tests for components that use permissions. Add missing tests and fix failures, checking that administrators can use mutation controls, auditors cannot, and read-only content remains visible to both.

  • Adding permission tests for a component
  • Checking admin and auditor behavior
  • Fixing failing permission-related tests
trycompai/comp2.1KUpdated Oct 24, 2026

Set up Better Auth for TypeScript applications, including server and client configuration, database adapters, sessions, email and social sign-in, and plugins. The guide also highlights environment variables, schema generation, and security settings to review.

  • Configuring Better Auth in a TypeScript app
  • Connecting an ORM or database adapter
  • Setting up sessions and cookie storage
trycompai/comp2.1KUpdated Oct 24, 2026

See all skills by trycompai

Report a problem with this skill