Comprehensive Rules for building robust, accessible, and performant front-end form validation with TypeScript, HTML5, and popular validation libraries.
Form validation drives users away faster than broken checkout flows. One poorly timed error message or unclear validation feedback can kill conversions, frustrate users, and create accessibility barriers that exclude entire user groups.
Here's what kills developer productivity with forms:
Context Switching Nightmare: You're jumping between validation libraries, accessibility specs, framework docs, and testing patterns. Each form becomes a research project instead of a straightforward implementation.
Error Handling Chaos: Managing validation state across blur events, change handlers, submission flows, and async checks creates brittle code that breaks when requirements change.
Accessibility Afterthought: ARIA attributes, screen reader compatibility, and keyboard navigation get bolted on last-minute, creating compliance gaps and poor user experiences.
Testing Complexity: Unit testing validators, integration testing form flows, and E2E testing submission scenarios requires coordinating multiple testing strategies.
These Cursor Rules transform form validation from a scattered research exercise into a systematic workflow. You get battle-tested patterns for HTML5 foundations, TypeScript safety, framework integration, and accessibility compliance—all in one unified approach.
Instead of researching validation patterns for each new form, you'll have consistent, production-ready implementations that handle edge cases, maintain accessibility standards, and integrate seamlessly with your existing codebase.
Eliminate Context Switching: Get framework-specific patterns for React Hook Form, Angular Reactive Forms, Vue Vuelidate, and vanilla HTML5 in one place. No more jumping between documentation sites to piece together validation strategies.
Bulletproof Error Handling: Structured error management that preserves user input, provides clear feedback, and handles both sync and async validation scenarios without breaking user flow.
Built-in Accessibility: ARIA attributes, screen reader compatibility, and keyboard navigation patterns built into every validation pattern. Ship compliant forms without accessibility expertise.
TypeScript-First Approach: Fully typed form schemas, validation functions, and error states that catch issues at compile time rather than in production.
Before: Building a user registration form means researching password strength validation, email format checking, async username availability, and coordinating validation timing across multiple steps.
With Cursor Rules:
// Generated schema with proper typing
interface SignUpFormData {
email: string;
username: string;
password: string;
}
// Auto-generated validators with debouncing
const validateUsername = debounce(async (username: string) => {
return await checkUsernameAvailability(username);
}, 300);
// Accessibility-ready error handling
<TextField
name="email"
aria-invalid={errors.email ? 'true' : 'false'}
aria-describedby={errors.email ? 'email-error' : undefined}
/>
{errors.email && (
<div id="email-error" role="alert" className="error-message">
{errors.email}
</div>
)}
Result: Complete form validation in 30 minutes instead of 3 hours of research and implementation.
Before: Managing validation for forms with dependent fields, dynamic validation rules, and complex business logic requires custom state management and error coordination.
With Cursor Rules:
// Schema with conditional validation
const businessFormSchema = z.object({
businessType: z.enum(['individual', 'company']),
taxId: z.string().optional(),
companyName: z.string().optional(),
}).refine((data) => {
if (data.businessType === 'company') {
return data.taxId && data.companyName;
}
return true;
}, {
message: "Company details required for business accounts",
path: ['companyName']
});
// Generated validation with proper error targeting
const { errors, handleSubmit } = useForm({
resolver: zodResolver(businessFormSchema),
mode: 'onBlur'
});
Result: Complex conditional validation implemented with type safety and proper error targeting in minutes, not hours.
Before: Implementing real-time validation that checks server-side constraints (like email uniqueness) while maintaining good UX requires careful debouncing, loading states, and error coordination.
With Cursor Rules:
// Auto-generated async validation pattern
const EmailField = () => {
const [isChecking, setIsChecking] = useState(false);
const validateEmailAvailability = useMemo(
() => debounce(async (email: string) => {
setIsChecking(true);
const isAvailable = await checkEmailAvailability(email);
setIsChecking(false);
return isAvailable;
}, 500),
[]
);
return (
<div className="field-group">
<input
type="email"
onChange={handleEmailChange}
aria-describedby="email-status"
/>
<div id="email-status" aria-live="polite">
{isChecking && "Checking availability..."}
{error && <span role="alert">{error}</span>}
</div>
</div>
);
};
Result: Production-ready async validation with proper loading states and accessibility in one implementation cycle.
# Install recommended dependencies
npm install zod @hookform/resolvers react-hook-form
# Create your forms directory structure
mkdir -p src/forms/sign-up/{validators,__tests__}
.cursor-rules fileCmd+L and ask: "Create a user registration form with email, password, and confirm password validation"The rules automatically adapt to your framework context:
Generate comprehensive tests with: "Create unit tests for the password strength validator and integration tests for the form submission flow"
// Auto-generated test patterns
describe('Password Validation', () => {
it('rejects passwords shorter than 8 characters', () => {
expect(isStrongPassword('weak')).toBe(false);
});
it('accepts strong passwords', () => {
expect(isStrongPassword('Strong123!')).toBe(true);
});
});
Development Speed: Build production-ready forms 5x faster with consistent patterns and generated boilerplate. No more researching validation approaches for each new form.
Code Quality: TypeScript-first validation with compile-time error catching. Proper separation of concerns with dedicated validator functions and schema definitions.
User Experience: Consistent validation timing, clear error messages, and preserved user input create frustration-free form interactions.
Accessibility Compliance: Built-in ARIA attributes, screen reader support, and keyboard navigation ensure your forms work for all users without additional accessibility research.
Maintenance: Centralized validation logic with reusable validators reduces technical debt and makes form updates straightforward.
Your forms become a competitive advantage instead of a development bottleneck. Users complete them successfully, accessibility audits pass automatically, and new forms ship with confidence instead of crossed fingers.
Ready to transform your form development workflow? Add these rules to your Cursor configuration and build your next form in minutes, not hours.
You are an expert in HTML5, TypeScript (ES2022), React (React Hook Form / Formik), Angular (Reactive Forms), Vue (Vuelidate / FormKit), schema validators (Zod, Yup, Joi), CSS-in-JS, Jest, and Cypress.
Key Principles
- Layer validation: HTML5 attributes → client-side schema → server-side checks.
- Fail fast: validate on change/blur, halt submission on first invalid field.
- Keep forms minimal; ask only for essential data.
- Display errors inline next to fields and in a summary after submission.
- Never rely on color alone; pair icons/aria-live regions for accessibility.
- Preserve user input on validation failure.
- Prefer immutable, typed form state; never mutate DOM directly.
- Write small, pure, reusable validation helpers.
JavaScript / TypeScript
- Use `interface` or `type` for every form data shape.
- All functions pure & typed: `function isEmail(value: string): boolean`.
- Destructure props; avoid `any`, implicit `any`, or `null` checks.
- Use `const` for validators; group them in `/validators` directory.
- File layout: `form-name/index.tsx` (component) → `schema.ts` (Zod) → `types.ts` → `validators/`.
- Default to strict ESLint/Prettier; enable `@typescript-eslint/consistent-type-imports`.
Error Handling & Validation
- Place guard clauses at the top:
```ts
if (!schema.safeParse(data).success) return setErrors(parseZod(schema, data));
```
- Run synchronous field validation on `blur`; async (e.g., username uniqueness) on `debounced` `change`.
- Keep error objects flat: `{ field: "message" }`.
- Include ARIA attributes: `aria-invalid`, `aria-describedby`.
- Add `role="alert"` to live error containers.
- Keep errors visible until they are corrected and re-validated.
- Throw user-friendly errors; never expose stack traces to UI.
Framework-Specific Rules
React (React Hook Form / Formik)
- Always use controlled components.
- Supply a schema resolver (e.g., `@hookform/resolvers/zod`).
- Extract field components: `<TextField name="email" label="Email" />`.
- Wrap submit handler with `handleSubmit` to auto-prevent default.
- Reset form with `reset()` after successful submission.
Angular (Reactive Forms)
- Build `FormGroup` with `FormBuilder`; provide synchronous and async validators.
- Mark controls as `touched` on blur.
- Use `statusChanges` observable to trigger side effects.
- Display global error summary with `*ngIf="form.invalid && form.touched"`.
Vue (Vuelidate / FormKit)
- Declare rules in `setup()` and return validators.
- Use `$errors` array for inline messages.
- Add `<FormKit type="form" @submit="onSubmit" />` for FormKit with schema prop.
Vanilla HTML5
- Always add `required`, `type="email"`, `pattern`, `min`, `max` where relevant.
- Use `novalidate` on `<form>` when custom JavaScript validation overrides default browser messages.
Additional Sections
Accessibility
- Add `<fieldset>` & `<legend>` for grouped controls.
- Link errors using `aria-describedby="email-error"`.
- Provide `lang` attribute on `<html>`.
- Ensure 4.5:1 contrast for error text.
Testing
- Unit: Jest tests for each validator.
```ts
test('rejects blank password', () => {
expect(isStrongPassword('')).toBe(false);
});
```
- Integration: React Testing Library—simulate user typing & blur events.
- E2E: Cypress—assert error visibility and successful submission.
Performance
- Debounce expensive async checks (250–500 ms).
- Lazy-load heavy validators only when form mounts.
- Memoize derived error summaries.
Security
- Sanitize input before display (e.g., DOMPurify).
- Never trust client-side validation; mirror rules on server.
- Rate-limit endpoints used by async validators.
Common Pitfalls & Remedies
- ❌ Showing all errors at once → ✅ Show first error per field.
- ❌ Clearing inputs on failure → ✅ Keep values; only highlight invalid.
- ❌ Relying solely on regex for complex data (e.g., email) → ✅ Use dedicated libraries or server lookups.
Directory Convention Example
```
/forms
└─ sign-up
├─ SignUpForm.tsx # exported component
├─ schema.ts # Zod schema
├─ types.ts # interfaces
├─ validators/
│ └─ password.ts
└─ __tests__/SignUpForm.test.tsx
```