
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
ctrovalidate
Advanced tools
The lightweight, declarative, and accessible form validation library for modern web apps. Zero-dependency, HTML-first, and ARIA-ready.
The lightweight, declarative, and accessible form validation library for modern web apps.
Ctrovalidate is a zero-dependency, TypeScript-native form validation library that bridges the gap between raw DOM power and framework-ready APIs. It embraces a declarative, HTML-first approach, allowing you to define validation rules directly in your markup using data attributes.
aria-invalid, aria-describedby).d.ts files# npm
npm install ctrovalidate
# yarn
yarn add ctrovalidate
# pnpm
pnpm add ctrovalidate
<form id="registrationForm" novalidate>
<div class="form-group">
<label for="email">Email Address</label>
<input
type="email"
name="email"
id="email"
data-ctrovalidate-rules="required|email"
placeholder="john@example.com"
/>
<div class="error-message"></div>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
type="password"
name="password"
id="password"
data-ctrovalidate-rules="required|strongPassword"
/>
<div class="error-message"></div>
</div>
<button type="submit">Register</button>
</form>
import { Ctrovalidate } from 'ctrovalidate';
// Initialize validator
const validator = new Ctrovalidate(
document.querySelector('#registrationForm'),
{
realTime: true, // Validate on blur/input
errorClass: 'is-invalid', // CSS class for invalid fields
pendingClass: 'is-validating', // CSS class during async validation
}
);
// Validate on submit
form.addEventListener('submit', async (e) => {
e.preventDefault();
const isValid = await validator.validate();
if (isValid) {
// Submit to your API
const formData = new FormData(form);
await fetch('/api/register', {
method: 'POST',
body: formData,
});
}
});
required - Field must have a valueemail - Valid email formatminLength:n - Minimum character lengthmaxLength:n - Maximum character lengthexactLength:n - Exact character lengthsameAs:fieldName - Must match another fieldalpha - Only alphabetic charactersalphaNum - Alphanumeric characters onlyalphaDash - Alphanumeric with dashes/underscoresurl - Valid URL formatphone - Valid phone numbercreditCard - Valid credit card (Luhn algorithm)strongPassword - Strong password requirementsjson - Valid JSON stringipAddress - Valid IPv4 or IPv6 addressnumeric - Any numeric valueinteger - Integer values onlydecimal - Decimal/float valuesmin:n - Minimum numeric valuemax:n - Maximum numeric valuebetween:min,max - Value within range// Validation
await validator.validate() // Validate entire form
// Field Management
validator.addField(element) // Add field dynamically
validator.removeField(element) // Remove field dynamically
validator.refresh() // Re-discover fields after DOM changes
// State Inspection
validator.isDirty('fieldName') // Check if field was touched
validator.getError('fieldName') // Get current error message
// Lifecycle
validator.reset() // Reset all validation states
validator.destroy() // Clean up validator instance
// Static Methods (Global)
Ctrovalidate.addRule(name, logic, message?) // Add custom rule
Ctrovalidate.addAsyncRule(name, logic, message?) // Add async rule
Ctrovalidate.setCustomMessages(messages) // Override messages
// Register async rule (e.g., check username availability)
Ctrovalidate.addAsyncRule(
'usernameAvailable',
async (value, params, element, signal) => {
const response = await fetch(`/api/check-username?username=${value}`, {
signal, // Abort if user types again
});
const data = await response.json();
return data.available;
},
'This username is already taken.'
);
<input name="username" data-ctrovalidate-rules="required|usernameAvailable" />
<!-- Controller field -->
<select name="contact_method">
<option value="email">Email</option>
<option value="phone">Phone</option>
</select>
<!-- Validates only if contact_method = "email" -->
<input
name="email"
data-ctrovalidate-rules="required|email"
data-ctrovalidate-if="contact_method:value:email"
/>
<!-- Validates only if contact_method = "phone" -->
<input
name="phone"
data-ctrovalidate-rules="required|phone"
data-ctrovalidate-if="contact_method:value:phone"
/>
// Add custom synchronous rule
Ctrovalidate.addRule(
'isCompanyEmail',
(value) => value.endsWith('@company.com'),
'Please use your company email address.'
);
// Override default error messages
Ctrovalidate.setCustomMessages({
required: 'This field cannot be empty!',
email: 'Please enter a valid email address.',
minLength: 'Please enter at least {0} characters.',
});
// Add field programmatically
const newInput = document.createElement('input');
newInput.name = 'additional_email';
newInput.setAttribute('data-ctrovalidate-rules', 'required|email');
form.appendChild(newInput);
validator.addField(newInput);
// Remove field
validator.removeField(newInput);
newInput.remove();
Ctrovalidate works seamlessly with all major frameworks:
| Framework | Demo | Documentation |
|---|---|---|
| Vanilla JS | demo-vanilla-js | Getting Started |
| React 18+ | demo-react | React Integration |
| Next.js 15+ | demo-nextjs | Next.js Integration |
| Vue 3 | demo-vue | Vue Integration |
| Alpine.js | demo-alpine | Alpine.js Integration |
| HTMX | - | HTMX Best Practices |
Check out our comprehensive demo that demonstrates every single feature:
# Install dependencies
npm install
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build library
npm run build
# Run documentation site locally
npm run docs:dev
# Lint code
npm run lint
# Format code
npm run format:fix
.d.ts filesWe welcome contributions! Please see our Contributing Guide for details.
git checkout -b feature/amazing-feature)npm run lint and npm testMIT © Ctrotech
Made with ❤️ by Ctrotech • Report Bug • Request Feature
FAQs
The lightweight, declarative, and accessible form validation library for modern web apps. Zero-dependency, HTML-first, and ARIA-ready.
We found that ctrovalidate demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.