form-validation
About
This skill provides comprehensive form validation solutions using popular libraries like React Hook Form, Formik, and Vee-Validate. It enables robust form handling with real-time validation feedback and TypeScript type safety. Use it when implementing user input validation, form submission handling, or complex multi-step forms.
Documentation
Form Validation
Overview
Implement comprehensive form validation including client-side validation, server-side synchronization, and real-time error feedback with TypeScript type safety.
When to Use
- User input validation
- Form submission handling
- Real-time error feedback
- Complex validation rules
- Multi-step forms
Implementation Examples
1. React Hook Form with TypeScript
// types/form.ts
export interface LoginFormData {
email: string;
password: string;
rememberMe: boolean;
}
export interface RegisterFormData {
email: string;
password: string;
confirmPassword: string;
name: string;
terms: boolean;
}
// components/LoginForm.tsx
import { useForm, SubmitHandler } from 'react-hook-form';
import { LoginFormData } from '../types/form';
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
export const LoginForm: React.FC = () => {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
watch
} = useForm<LoginFormData>({
defaultValues: {
email: '',
password: '',
rememberMe: false
}
});
const onSubmit: SubmitHandler<LoginFormData> = async (data) => {
try {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(data)
});
if (!response.ok) throw new Error('Login failed');
// Handle success
} catch (error) {
console.error(error);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Email</label>
<input
type="email"
{...register('email', {
required: 'Email is required',
pattern: {
value: emailRegex,
message: 'Invalid email format'
}
})}
/>
{errors.email && <span className="error">{errors.email.message}</span>}
</div>
<div>
<label>Password</label>
<input
type="password"
{...register('password', {
required: 'Password is required',
minLength: {
value: 8,
message: 'Password must be at least 8 characters'
}
})}
/>
{errors.password && <span className="error">{errors.password.message}</span>}
</div>
<div>
<label>
<input type="checkbox" {...register('rememberMe')} />
Remember me
</label>
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Logging in...' : 'Login'}
</button>
</form>
);
};
// Custom validator
const usePasswordStrength = () => {
return (password: string): boolean | string => {
if (password.length < 8) return 'At least 8 characters';
if (!/[A-Z]/.test(password)) return 'At least one uppercase letter';
if (!/[0-9]/.test(password)) return 'At least one number';
return true;
};
};
2. Formik with Yup Validation
// validationSchema.ts
import * as Yup from 'yup';
export const registerValidationSchema = Yup.object().shape({
email: Yup.string()
.email('Invalid email')
.required('Email is required'),
password: Yup.string()
.min(8, 'Password must be at least 8 characters')
.matches(/[A-Z]/, 'Must contain uppercase letter')
.matches(/[0-9]/, 'Must contain number')
.required('Password is required'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Passwords must match')
.required('Confirm password is required'),
name: Yup.string()
.min(2, 'Name too short')
.required('Name is required'),
terms: Yup.boolean()
.oneOf([true], 'You must accept terms')
.required()
});
// components/RegisterForm.tsx
import { Formik, Form, Field, ErrorMessage } from 'formik';
import { registerValidationSchema } from '../validationSchema';
import { RegisterFormData } from '../types/form';
export const RegisterForm: React.FC = () => {
const initialValues: RegisterFormData = {
email: '',
password: '',
confirmPassword: '',
name: '',
terms: false
};
const handleSubmit = async (
values: RegisterFormData,
{ setSubmitting, setFieldError }: any
) => {
try {
const response = await fetch('/api/register', {
method: 'POST',
body: JSON.stringify(values)
});
if (!response.ok) {
const error = await response.json();
if (error.emailExists) {
setFieldError('email', 'Email already registered');
}
throw new Error(error.message);
}
} catch (error) {
console.error(error);
} finally {
setSubmitting(false);
}
};
return (
<Formik
initialValues={initialValues}
validationSchema={registerValidationSchema}
onSubmit={handleSubmit}
>
{({ isSubmitting, isValid }) => (
<Form>
<div>
<label htmlFor="name">Name</label>
<Field name="name" type="text" />
<ErrorMessage name="name" component="span" className="error" />
</div>
<div>
<label htmlFor="email">Email</label>
<Field name="email" type="email" />
<ErrorMessage name="email" component="span" className="error" />
</div>
<div>
<label htmlFor="password">Password</label>
<Field name="password" type="password" />
<ErrorMessage name="password" component="span" className="error" />
</div>
<div>
<label htmlFor="confirmPassword">Confirm Password</label>
<Field name="confirmPassword" type="password" />
<ErrorMessage name="confirmPassword" component="span" className="error" />
</div>
<div>
<label>
<Field name="terms" type="checkbox" />
I agree to terms
</label>
<ErrorMessage name="terms" component="span" className="error" />
</div>
<button type="submit" disabled={isSubmitting || !isValid}>
{isSubmitting ? 'Registering...' : 'Register'}
</button>
</Form>
)}
</Formik>
);
};
3. Vue Vee-Validate
// validationRules.ts
import { defineRule } from 'vee-validate';
import { email, required, min, confirmed } from '@vee-validate/rules';
defineRule('required', required);
defineRule('email', email);
defineRule('min', min);
defineRule('confirmed', confirmed);
defineRule('password-strength', (value: string) => {
if (value.length < 8) return 'Password must be at least 8 characters';
if (!/[A-Z]/.test(value)) return 'Must contain uppercase letter';
if (!/[0-9]/.test(value)) return 'Must contain number';
return true;
});
// components/LoginForm.vue
<template>
<Form @submit="onSubmit" :validation-schema="validationSchema">
<div class="form-group">
<label for="email">Email</label>
<Field name="email" type="email" as="input" class="form-control" />
<ErrorMessage name="email" class="error" />
</div>
<div class="form-group">
<label for="password">Password</label>
<Field name="password" type="password" as="input" class="form-control" />
<ErrorMessage name="password" class="error" />
</div>
<button type="submit" :disabled="isSubmitting">
{{ isSubmitting ? 'Logging in...' : 'Login' }}
</button>
</Form>
</template>
<script setup lang="ts">
import { Form, Field, ErrorMessage } from 'vee-validate';
import { object, string } from 'yup';
import { ref } from 'vue';
const isSubmitting = ref(false);
const validationSchema = object({
email: string().email('Invalid email').required('Email is required'),
password: string().required('Password is required')
});
const onSubmit = async (values: any) => {
isSubmitting.value = true;
try {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(values)
});
if (!response.ok) throw new Error('Login failed');
} catch (error) {
console.error(error);
} finally {
isSubmitting.value = false;
}
};
</script>
4. Custom Validator Hook
// hooks/useFieldValidator.ts
import { useState, useCallback } from 'react';
export interface ValidationRule {
validate: (value: any) => boolean | string;
message: string;
}
export interface FieldError {
isValid: boolean;
message: string | null;
}
export const useFieldValidator = (rules: ValidationRule[] = []) => {
const [error, setError] = useState<FieldError>({
isValid: true,
message: null
});
const validate = useCallback((value: any) => {
for (const rule of rules) {
const result = rule.validate(value);
if (result !== true) {
setError({
isValid: false,
message: typeof result === 'string' ? result : rule.message
});
return false;
}
}
setError({
isValid: true,
message: null
});
return true;
}, [rules]);
const clearError = useCallback(() => {
setError({
isValid: true,
message: null
});
}, []);
return { error, validate, clearError };
};
// Usage
const { error: emailError, validate: validateEmail } = useFieldValidator([
{
validate: (v) => v.length > 0,
message: 'Email is required'
},
{
validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
message: 'Invalid email format'
}
]);
5. Server-Side Validation Integration
// Async server validation
const useAsyncValidation = () => {
const validateEmail = async (email: string) => {
const response = await fetch(`/api/validate/email?email=${email}`);
const { available } = await response.json();
return available ? true : 'Email already registered';
};
const validateUsername = async (username: string) => {
const response = await fetch(`/api/validate/username?username=${username}`);
const { available } = await response.json();
return available ? true : 'Username taken';
};
return { validateEmail, validateUsername };
};
// React Hook Form with async validation
const { validateEmail } = useAsyncValidation();
register('email', {
required: 'Email required',
validate: async (value) => {
return await validateEmail(value);
}
});
Best Practices
- Validate on both client and server
- Provide real-time feedback
- Use TypeScript for type safety
- Implement custom validators for complex rules
- Handle async validation properly
- Show clear error messages
- Preserve user input on validation failure
- Test validation rules thoroughly
- Use schema validation (Yup, Zod)
Resources
Quick Install
/plugin add https://github.com/aj-geddes/useful-ai-prompts/tree/main/form-validationCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
Algorithmic Art Generation
MetaThis skill helps developers create algorithmic art using p5.js, focusing on generative art, computational aesthetics, and interactive visualizations. It automatically activates for topics like "generative art" or "p5.js visualization" and guides you through creating unique algorithms with features like seeded randomness, flow fields, and particle systems. Use it when you need to build reproducible, code-driven artistic patterns.
webapp-testing
TestingThis Claude Skill provides a Playwright-based toolkit for testing local web applications through Python scripts. It enables frontend verification, UI debugging, screenshot capture, and log viewing while managing server lifecycles. Use it for browser automation tasks but run scripts directly rather than reading their source code to avoid context pollution.
finishing-a-development-branch
TestingThis skill helps developers complete finished work by verifying tests pass and then presenting structured integration options. It guides the workflow for merging, creating PRs, or cleaning up branches after implementation is done. Use it when your code is ready and tested to systematically finalize the development process.
