Form Validation Expert logo

Form Validation Expert

Community
roedyrustam
form-validation-expert

Form validation with React Hook Form, Zod, and server actions / Validasi formulir dengan React Hook Form, Zod, dan server actions.

Overview

Publisherroedyrustam
Repositoryvibes-plug
Skill nameform-validation-expert
Stars
117
Forks
27
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by roedyrustam on GitHub. Read the source before you install it.

Installation

Install the Form Validation Expert AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/roedyrustam/vibes-plug/tree/main/skills/form-validation-expert
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/roedyrustam/vibes-plug.git /tmp/vibes-plug
mkdir -p .claude/skills
cp -r /tmp/vibes-plug/skills/form-validation-expert .claude/skills/form-validation-expert
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Form Validation Expert in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Form Validation Expert on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Form Validation Expert is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Form & Validation Expert (2026 Edition)

English | Bahasa Indonesia


English

Orchestration & Integration

Connects and orchestrates with relevant domain skills:

  • modern-web-guidance: Evaluates native form validation features (:user-valid, :user-invalid), autofill tokens, and custom selects.
  • modern-css-native-expert: Leverages field-sizing: content for dynamic textareas and pure CSS validation feedback.
  • design-system-architect: Provides accessible form control primitives, input borders, and floating label tokens.
  • anti-slop: Eliminates vague error messages, double-submission bugs, and unhandled pending states.
  • senior-frontend: React 19 useActionState, useOptimistic, and server action form submission.
  • brainstorming, zero-to-prod-orchestrator, session-memory-manager: Continuous lifecycle execution.

Description

Production-grade guide for building robust, accessible, and user-friendly forms. Covers React Hook Form v7+ with Zod/Valibot resolvers, server-side validation with useActionState + Zod, multi-step form wizards, dynamic forms from JSON Schema, autofill-friendly patterns, optimistic form submission with useOptimistic, Conform (progressive enhancement), and form accessibility (ARIA, error announcements).

Trigger Conditions

Activate this skill when:

  • Building forms with client-side validation (React Hook Form, Zod, Valibot).
  • Implementing server-side form validation with React 19 Server Actions.
  • Creating multi-step form wizards with state persistence.
  • Building dynamic forms generated from schema definitions.
  • Making forms accessible (ARIA attributes, error announcements).
  • Implementing file upload forms with drag-and-drop.
  • Optimizing forms for autofill and mobile usability.

Form Library Selection Guide

LibraryBest ForKey Strength
React Hook Form + ZodMost React appsPerformance (uncontrolled), rich ecosystem
ConformProgressive enhancement, RSCWorks without JS, native validation
FormikLegacy projectsMature, large community (declining)
TanStack FormComplex, headless formsFramework-agnostic, type-safe

Recommendation: Use React Hook Form + Zod for most projects. Use Conform for Next.js Server Actions with progressive enhancement.


1. React Hook Form + Zod (Client-Side)

tsx
// components/signup-form.tsx
'use client';

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

const signupSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Please enter a valid email'),
  password: z.string()
    .min(8, 'Password must be at least 8 characters')
    .regex(/[A-Z]/, 'Must contain an uppercase letter')
    .regex(/[0-9]/, 'Must contain a number'),
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: 'Passwords do not match',
  path: ['confirmPassword'],
});

type SignupFormData = z.infer<typeof signupSchema>;

export function SignupForm() {
  const {
    register,
    handleSubmit,
    formState: { errors, isSubmitting },
  } = useForm<SignupFormData>({
    resolver: zodResolver(signupSchema),
    mode: 'onBlur', // Validate on blur for better UX
  });

  const onSubmit = async (data: SignupFormData) => {
    const res = await fetch('/api/auth/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    if (!res.ok) throw new Error('Signup failed');
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <div>
        <label htmlFor="name">Name</label>
        <input
          id="name"
          {...register('name')}
          aria-invalid={!!errors.name}
          aria-describedby={errors.name ? 'name-error' : undefined}
          autoComplete="name"
        />
        {errors.name && <p id="name-error" role="alert">{errors.name.message}</p>}
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <input
          id="email"
          type="email"
          {...register('email')}
          aria-invalid={!!errors.email}
          aria-describedby={errors.email ? 'email-error' : undefined}
          autoComplete="email"
        />
        {errors.email && <p id="email-error" role="alert">{errors.email.message}</p>}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input
          id="password"
          type="password"
          {...register('password')}
          aria-invalid={!!errors.password}
          aria-describedby={errors.password ? 'password-error' : undefined}
          autoComplete="new-password"
        />
        {errors.password && <p id="password-error" role="alert">{errors.password.message}</p>}
      </div>

      <div>
        <label htmlFor="confirmPassword">Confirm Password</label>
        <input
          id="confirmPassword"
          type="password"
          {...register('confirmPassword')}
          aria-invalid={!!errors.confirmPassword}
          aria-describedby={errors.confirmPassword ? 'confirm-error' : undefined}
          autoComplete="new-password"
        />
        {errors.confirmPassword && <p id="confirm-error" role="alert">{errors.confirmPassword.message}</p>}
      </div>

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? 'Creating account...' : 'Sign Up'}
      </button>
    </form>
  );
}

2. Server Actions + useActionState (React 19 / Next.js 15)

typescript
// actions/signup.ts
'use server';

import { z } from 'zod';

const signupSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  password: z.string().min(8),
});

export type SignupState = {
  errors?: Record<string, string[]>;
  message?: string;
  success?: boolean;
};

export async function signupAction(
  prevState: SignupState,
  formData: FormData,
): Promise<SignupState> {
  const parsed = signupSchema.safeParse({
    name: formData.get('name'),
    email: formData.get('email'),
    password: formData.get('password'),
  });

  if (!parsed.success) {
    return {
      errors: parsed.error.flatten().fieldErrors as Record<string, string[]>,
      message: 'Please fix the errors below.',
    };
  }

  // Check if email already exists
  const existing = await db.query.users.findFirst({
    where: eq(users.email, parsed.data.email),
  });
  if (existing) {
    return { errors: { email: ['This email is already registered'] } };
  }

  // Create user
  await createUser(parsed.data);
  return { success: true, message: 'Account created!' };
}
tsx
// components/signup-form-server.tsx
'use client';

import { useActionState } from 'react';
import { signupAction, type SignupState } from '@/actions/signup';

export function SignupFormServer() {
  const [state, formAction, isPending] = useActionState<SignupState, FormData>(
    signupAction,
    {},
  );

  return (
    <form action={formAction}>
      {state.message && (
        <div role="alert" className={state.success ? 'success' : 'error'}>
          {state.message}
        </div>
      )}

      <div>
        <label htmlFor="name">Name</label>
        <input id="name" name="name" required autoComplete="name" />
        {state.errors?.name && <p role="alert">{state.errors.name[0]}</p>}
      </div>

      <div>
        <label htmlFor="email">Email</label>
        <input id="email" name="email" type="email" required autoComplete="email" />
        {state.errors?.email && <p role="alert">{state.errors.email[0]}</p>}
      </div>

      <div>
        <label htmlFor="password">Password</label>
        <input id="password" name="password" type="password" required minLength={8} autoComplete="new-password" />
        {state.errors?.password && <p role="alert">{state.errors.password[0]}</p>}
      </div>

      <button type="submit" disabled={isPending}>
        {isPending ? 'Creating...' : 'Sign Up'}
      </button>
    </form>
  );
}

3. Multi-Step Form Wizard

tsx
// components/multi-step-form.tsx
'use client';

import { useState, useCallback } from 'react';
import { useForm, FormProvider, type UseFormReturn } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

// Step schemas
const step1Schema = z.object({
  companyName: z.string().min(1, 'Required'),
  industry: z.string().min(1, 'Required'),
});

const step2Schema = z.object({
  contactName: z.string().min(1, 'Required'),
  contactEmail: z.string().email(),
});

const step3Schema = z.object({
  plan: z.enum(['starter', 'pro', 'enterprise']),
  billingCycle: z.enum(['monthly', 'annual']),
});

const fullSchema = step1Schema.merge(step2Schema).merge(step3Schema);
type FormData = z.infer<typeof fullSchema>;

const STEP_SCHEMAS = [step1Schema, step2Schema, step3Schema] as const;
const STEP_TITLES = ['Company Info', 'Contact Details', 'Choose Plan'] as const;

export function MultiStepForm() {
  const [currentStep, setCurrentStep] = useState(0);
  const methods = useForm<FormData>({
    resolver: zodResolver(fullSchema),
    mode: 'onChange',
  });

  const goNext = useCallback(async () => {
    const schema = STEP_SCHEMAS[currentStep];
    const fields = Object.keys(schema.shape) as (keyof FormData)[];
    const isValid = await methods.trigger(fields);
    if (isValid) setCurrentStep(s => Math.min(s + 1, STEP_SCHEMAS.length - 1));
  }, [currentStep, methods]);

  const goBack = useCallback(() => {
    setCurrentStep(s => Math.max(s - 1, 0));
  }, []);

  const onSubmit = async (data: FormData) => {
    await fetch('/api/onboarding', {
      method: 'POST',
      body: JSON.stringify(data),
    });
  };

  return (
    <FormProvider {...methods}>
      {/* Progress indicator */}
      <nav aria-label="Form progress">
        <ol className="step-progress">
          {STEP_TITLES.map((title, i) => (
            <li key={i} className={i === currentStep ? 'active' : i < currentStep ? 'completed' : ''}>
              {title}
            </li>
          ))}
        </ol>
      </nav>

      <form onSubmit={methods.handleSubmit(onSubmit)}>
        {currentStep === 0 && <Step1 />}
        {currentStep === 1 && <Step2 />}
        {currentStep === 2 && <Step3 />}

        <div className="button-group">
          {currentStep > 0 && <button type="button" onClick={goBack}>Back</button>}
          {currentStep < STEP_SCHEMAS.length - 1
            ? <button type="button" onClick={goNext}>Next</button>
            : <button type="submit">Submit</button>
          }
        </div>
      </form>
    </FormProvider>
  );
}

4. Form Accessibility Checklist

RequirementImplementation
LabelsEvery input has a visible <label> with htmlFor matching input id
Error identificationaria-invalid="true" on invalid fields
Error descriptionaria-describedby pointing to error message element
Error announcementError messages use role="alert" for screen readers
Required fieldsUse aria-required="true" or native required attribute
AutofillCorrect autoComplete values (name, email, new-password)
Focus managementFocus moves to first error on submit failure
Submit stateButton shows loading state and is disabled during submission

Material Design 3 (M3) Text Field Architecture (https://m3.material.io/components/text-fields/overview)

M3 defines two canonical text field variants with precise interactive state layers, animated floating labels, and accessible error binding:

1. M3 Outlined Text Field Component
tsx
import * as React from 'react';
import { useFormContext } from 'react-hook-form';

interface M3TextFieldProps extends React.InputHTMLAttributes<HTMLInputElement> {
  name: string;
  label: string;
  helperText?: string;
  leadingIcon?: React.ReactNode;
  trailingIcon?: React.ReactNode;
}

export function M3OutlinedTextField({
  name,
  label,
  helperText,
  leadingIcon,
  trailingIcon,
  ...props
}: M3TextFieldProps) {
  const { register, formState: { errors } } = useFormContext();
  const error = errors[name]?.message as string | undefined;
  const inputId = `m3-input-${name}`;
  const helperId = `m3-helper-${name}`;

  return (
    <div className="relative w-full text-left">
      <div className="relative flex items-center">
        {leadingIcon && (
          <span className="absolute left-3 text-slate-500 pointer-events-none">
            {leadingIcon}
          </span>
        )}
        <input
          id={inputId}
          placeholder=" " /* Required for :placeholder-shown floating label */
          {...register(name)}
          {...props}
          aria-invalid={!!error}
          aria-describedby={helperId}
          className={`peer w-full h-14 bg-transparent rounded-md border text-base text-slate-900 dark:text-slate-100 transition-colors
            focus:outline-none focus:ring-0
            ${leadingIcon ? 'pl-10' : 'pl-4'} ${trailingIcon ? 'pr-10' : 'pr-4'} pt-4 pb-1
            ${error 
              ? 'border-red-600 focus:border-red-600' 
              : 'border-slate-400 hover:border-slate-600 focus:border-indigo-600 dark:border-slate-600 dark:focus:border-indigo-400'
            }`}
        />
        <label
          htmlFor={inputId}
          className={`absolute text-sm duration-200 transform origin-[0] pointer-events-none transition-all
            ${leadingIcon ? 'left-10' : 'left-4'}
            top-4 peer-focus:top-1.5 peer-focus:scale-75
            peer-[:not(:placeholder-shown)]:top-1.5 peer-[:not(:placeholder-shown)]:scale-75
            ${error ? 'text-red-600' : 'text-slate-500 peer-focus:text-indigo-600 dark:peer-focus:text-indigo-400'}`}
        >
          {label}
        </label>
        {trailingIcon && (
          <span className="absolute right-3 text-slate-500">
            {trailingIcon}
          </span>
        )}
      </div>

      <div id={helperId} className="flex justify-between px-4 mt-1 text-xs min-h-[16px]">
        {error ? (
          <span role="alert" className="text-red-600 font-medium">{error}</span>
        ) : helperText ? (
          <span className="text-slate-500">{helperText}</span>
        ) : null}
      </div>
    </div>
  );
}

Common Pitfalls to Avoid

Anti-PatternProblemCorrect Approach
Validating only on submitLate error discovery, poor UXValidate onBlur or onChange
Client-side validation onlyCan be bypassedAlways validate server-side too
No autoComplete attributesMisses browser autofillAdd correct autoComplete values
Generic error messages"Invalid field" doesn't helpSpecific: "Must be at least 8 characters"
Resetting form on errorUser loses all inputPreserve input, highlight errors
No loading state on submitUser clicks multiple timesDisable button + show spinner

Sovereign Anti-Slop Form Directives

Zero-tolerance standards for form engineering:

  1. Zero Vague Error Copy: Banning generic "Invalid field", "Input error", or "Something went wrong". Every validation rule must specify what failed and how to fix it (e.g., "Password must be at least 8 characters and include at least one number").
  2. Double-Submission Lock & Accessible Pending State: Submit buttons must be disabled while a request is in flight (disabled={isSubmitting || isPending}), display an inline accessible spinner, and declare aria-busy="true".
  3. A11y Field Error Binding: Every input in an error state must bind aria-invalid="true" and aria-describedby="{field}-error", with the error container declared as role="alert".
  4. Input Preservation Law: Never reset, wipe, or clear user inputs when validation errors or server exceptions occur. Keep user data intact so they can correct errors without retyping.
  5. End-to-End Schema Contract Parity: Never rely solely on client-side validation. Server actions and API routes must validate request payloads against the identical Zod/Valibot schema.

Sovereign Execution, Token Efficiency & Invariants

DimensionSovereign Mandate & Deterministic Contract
Frontier Model CascadeMedium / High (Gemini 4 Argon, Claude Sonnet 5.5 High, Muse Wonder High, Composer 2.5 Fast)
Availability FallbackGemini 4 Argon/Pro -> Gemini 3.8 Flash; Claude Opus 5.5 -> Claude Opus 4.6 (Zero unhandled missing-model errors)
KV-Cache & Token ProtocolPin static instructions & schemas at prompt prefix (kv-cache-prefix-optimizer); pass surgical CONTEXT_MAP.md slices (<250 tokens/call) during Swarm Fan-Out
SaaT CLI & MCP DispatchCLI: vibes skill exec form-validation-expert <audit|eval|verify|generate|blueprint> / MCP: vibes_skill_dispatch, vibes_visual_audit, vibes_ast_query, vibes_verify_gate
NFR & Anti-Slop InvariantsLCP < 1.8s, INP < 100ms, CLS < 0.05, 1px crisp borders, 3-tier radius (6px/8px/12px), tabular-nums, 5-state interactive contract; 100% complete zero-placeholder output verified via node scripts/check-anti-slop.js --strict


Bahasa Indonesia

Integrasi Orkestrasi

Terhubung dan mengorkestrasi skill domain yang relevan:

  • modern-web-guidance: Mengevaluasi fitur validasi form native (:user-valid, :user-invalid), token autofill, dan custom select.
  • modern-css-native-expert: Memanfaatkan field-sizing: content untuk textarea dinamis dan umpan balik validasi CSS murni.
  • design-system-architect: Menyediakan primitif kontrol form aksesibel, border input, dan token floating label.
  • anti-slop: Melenyapkan pesan error samar, bug pengiriman ganda, dan status pending yang tidak ditangani.
  • senior-frontend: Mengimplementasikan useActionState, useOptimistic, dan pengiriman form Server Actions di React 19.
  • brainstorming, zero-to-prod-orchestrator, session-memory-manager: Eksekusi siklus hidup proyek secara terpadu.

Deskripsi

Panduan tingkat produksi untuk membangun formulir yang kuat, aksesibel, dan ramah pengguna. Mencakup React Hook Form v7+ dengan Zod/Valibot resolver, validasi server-side dengan useActionState + Zod, wizard formulir multi-langkah, formulir dinamis dari JSON Schema, pola autofill-friendly, optimistic form submission, Conform (progressive enhancement), dan aksesibilitas formulir (ARIA, pengumuman error).

Kondisi Pemicu

Aktifkan skill ini ketika:

  • Membangun formulir dengan validasi client-side (React Hook Form, Zod, Valibot).
  • Mengimplementasikan validasi formulir server-side dengan React 19 Server Actions.
  • Membuat wizard formulir multi-langkah dengan persistensi state.
  • Membangun formulir dinamis dari definisi skema.
  • Membuat formulir yang aksesibel (atribut ARIA, pengumuman error).

Arsitektur Text Field Material Design 3 (M3) (https://m3.material.io/components/text-fields/overview)

M3 menetapkan dua format input utama (Filled dan Outlined) dengan animasi floating label dan indikator status:

  • Outlined Text Field: Garis batas perimetral bersih (border-outline), label mengapung saat fokus atau terisi (peer-focus:top-1.5 peer-focus:scale-75).
  • Filled Text Field: Latar belakang kontainer surface-container-highest dengan garis indikator aktif di bagian bawah (2px saat fokus).
  • Aksesibilitas & Ikon: Dukungan ikon leading/trailing (misal tombol visibilitas kata sandi), helper text terikat via aria-describedby, dan error alert berkontras tinggi yang lolos uji WCAG.

Arahan Anti-Slop Formulir

Standar nol toleransi untuk rekayasa formulir:

  1. Dilarang Menulis Pesan Error Samar: Melarang pesan generik "Field tidak valid", "Input salah", atau "Terjadi kesalahan". Setiap aturan validasi wajib menjelaskan apa yang keliru dan cara memperbaikinya (misal: "Kata sandi minimal 8 karakter dan mengandung minimal satu angka").
  2. Kunci Double-Submission & Indikator Pending Aksesibel: Tombol submit wajib dinonaktifkan selama proses pengiriman (disabled={isSubmitting || isPending}), menampilkan spinner inline, dan memiliki atribut aria-busy="true".
  3. Pengikatan Error ARIA pada Field: Setiap input yang berada dalam status error wajib menyematkan aria-invalid="true" dan aria-describedby="{field}-error", serta membungkus pesan error dengan role="alert".
  4. Hukum Perlindungan Input Pengguna: Dilarang mengosongkan atau menghapus input pengguna saat validasi gagal atau server error. Biarkan data pengguna tetap ada agar mereka dapat memperbaikinya tanpa mengetik ulang dari awal.
  5. Paritas Kontrak Skema End-to-End: Dilarang hanya mengandalkan validasi di sisi klien. Server action atau rute API wajib memvalidasi payload request terhadap skema Zod/Valibot yang identik.

Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian

DimensiMandat Berdaulat & Kontrak Deterministik
Kaskade Model FrontierMedium / High (Gemini 4 Argon, Claude Sonnet 5.5 High, Muse Wonder High, Composer 2.5 Fast)
Invarian Fallback KetersediaanGemini 4 Argon/Pro -> Gemini 3.8 Flash; Claude Opus 5.5 -> Claude Opus 4.6 (Bebas gagal saat model belum tersedia/kuota penuh)
Protokol Hemat Token & KV-CacheKunci instruksi & skema statis di prefix awal (kv-cache-prefix-optimizer); kirim potongan CONTEXT_MAP.md (<250 token/panggilan) saat Fan-Out
Eksekusi SaaT CLI & MCPCLI: vibes skill exec form-validation-expert <audit|eval|verify|generate|blueprint> / MCP: vibes_skill_dispatch, vibes_visual_audit, vibes_ast_query, vibes_verify_gate
Invarian NFR & Anti-SlopLCP < 1.8s, INP < 100ms, CLS < 0.05, border tegas 1px, radius 3-tier (6px/8px/12px), tabular-nums, kontrak interaktif 5-state; kode 100% tuntas tanpa placeholder diverifikasi melalui node scripts/check-anti-slop.js --strict

Frequently asked questions

What does the Form Validation Expert AI skill do?

Form validation with React Hook Form, Zod, and server actions / Validasi formulir dengan React Hook Form, Zod, dan server actions.

Why use Form Validation Expert on TypingMind?

Because you install it once and use it with any model. Form Validation Expert is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Form Validation Expert in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/roedyrustam/vibes-plug/tree/main/skills/form-validation-expert. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Form Validation Expert?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Form Validation Expert?

As many as you like. As long as a model supports skills, you can use Form Validation Expert with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Form Validation Expert AI skill free?

Yes. It is published on GitHub by roedyrustam under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇