Communitygithub.com

sakilanwar12/nextjs-typescript-skills

Use when building forms with react-hook-form and zod — schema-first validation, typed fields, server-action submit, accessible errors, and file inputs.

What is nextjs-typescript-skills?

nextjs-typescript-skills is a Claude Code agent skill that use when building forms with react-hook-form and zod — schema-first validation, typed fields, server-action submit, accessible errors, and file inputs.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/sakilanwar12/nextjs-typescript-skills/tree/HEAD/skills/nextjs-forms

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Forms (react-hook-form + zod)

Rules

  1. Schema first: define zod schema, derive type via z.infer. Single source of truth shared by client + server.
  2. Use @hookform/resolvers/zod (zodResolver). Mode onTouched for UX; validate on server again in the action.
  3. Type the form: useForm<FormValues>. No any field values.
  4. Accessible errors: aria-invalid, aria-describedby, role="alert" on messages. Associate <label htmlFor>.
  5. Submit via Server Action (formAction or action calling the server function). Show pending + server error states.

Pattern

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

const Schema = z.object({
  email: z.string().email('Enter a valid email'),
  password: z.string().min(8, 'Min 8 characters'),
});
type Values = z.infer<typeof Schema>;

export function LoginForm({ onSubmit }: { onSubmit: (v: Values) => Promise<{ ok: boolean; error?: string }> }) {
  const { register, handleSubmit, formState: { errors, isSubmitting }, setError } = useForm<Values>({
    resolver: zodResolver(Schema),
    mode: 'onTouched',
  });

  return (
    <form onSubmit={handleSubmit(async (v) => {
      const r = await onSubmit(v);
      if (!r.ok) setError('root', { message: r.error ?? 'Failed' });
    })}>
      <label htmlFor="email">Email</label>
      <input id="email" type="email" autoComplete="email"
        aria-invalid={!!errors.email} aria-describedby={errors.email ? 'email-err' : undefined}
        {...register('email')} />
      {errors.email && <p id="email-err" role="alert">{errors.email.message}</p>}

      <label htmlFor="password">Password</label>
      <input id="password" type="password" autoComplete="current-password" {...register('password')} />
      {errors.password && <p role="alert">{errors.password.message}</p>}

      {errors.root && <p role="alert">{errors.root.message}</p>}
      <button disabled={isSubmitting}>{isSubmitting ? 'Signing in…' : 'Sign in'}</button>
    </form>
  );
}

Checklist

  • zod schema + z.infer type?
  • zodResolver wired?
  • Labels + aria attributes on every field?
  • Server re-validates the same schema?
  • Pending disabled state + root/server errors shown?

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Related Skills