Search for a command to run...
Last updated June 9, 2026
Ctrovalidate tracks field state as a combination of internal flags and derived visual states. Understanding these helps you build predictable UIs.
Each field's internal state tracks only one boolean:
| State | Type | Description |
|---|---|---|
isDirty | boolean | true after first blur or input event on the field |
The FieldState object also stores the AbortController (for async rule cancellation) and lastError (most recent error string), but these are internal implementation details.
valid / invalid are not tracked as separate booleans. They are derived:
The browser controller maps states to CSS classes:
| State | CSS Class | Trigger |
|---|---|---|
| Invalid | errorClass (default 'is-invalid') | Added when validation fails, removed on success |
| Pending async | pendingClass (default 'ctrovalidate-pending') | Added during async rule execution, removed on completion |
const validator = new Ctrovalidate(form, {
errorClass: 'border-red-500',
pendingClass: 'opacity-50',
});Framework hooks expose four reactive states:
| State | Type | Description |
|---|---|---|
errors | Record of field → string | undefined | Error message per field. undefined means valid. |
isDirty | Record of field → boolean | Set to true by handleChange or handleBlur. |
isValidating | Record of field → boolean | true while an async rule is in flight for that field. |
isValid | boolean (Vue/Svelte only) | Derived — true when no field has an error. In React, check Object.values(errors).some(Boolean). |
The browser controller automatically manages:
aria-invalid="true" — Added when a field fails validation, removed on success or reset.aria-describedby — Linked to the error container's ID for screen reader announcements.role="status" and aria-live="polite" — Set on auto-generated error containers.<!-- After validation failure -->
<input name="email" aria-invalid="true" aria-describedby="ctrovalidate-error-email" />
<div id="ctrovalidate-error-email" role="status" aria-live="polite">
Please enter a valid email address.
</div>// Check if user has interacted with a field
if (validator.isDirty('email')) {
const error = validator.getError('email');
console.log('Error:', error);
}
// Trigger full validation
const isValid = await validator.validate();
// Reset all state
validator.reset();input events only validate if the field is already dirty.pendingClass to show spinners during async validation.validator.reset() or reset() (hooks) when clearing a form to wipe dirty flags and errors simultaneously.