Search for a command to run...
Last updated June 9, 2026
Ctrovalidate and Alpine.js share a common philosophy: Keep logic in the HTML. This synergy makes them a perfect pair for building interactive forms with minimal JavaScript overhead.
Initialize Ctrovalidate inside Alpine's x-init directive and store the instance in your x-data object for programmatic access.
<div
x-data="{
validator: null,
async submit() {
if (await this.validator.validateForm()) {
console.log('Valid! Proceeding with update.');
}
}
}"
x-init="validator = new Ctrovalidate($refs.form, { realTime: true })"
>
<form x-ref="form" @submit.prevent="submit" novalidate>
<div class="field">
<input name="email" data-ctrovalidate-rules="required|email" />
<div class="error-msg"></div>
</div>
<button type="submit">Sign Up</button>
</form>
</div>When adding or removing fields dynamically with x-for, you must notify Ctrovalidate to re-scan the DOM using validator.refresh(). Always wrap this call in $nextTick to ensure Alpine has finished updating the DOM.
<button @click="items.push({}); $nextTick(() => validator.refresh())">
Add Row
</button>x-ref: Use Alpine's $refs directly in the Ctrovalidate constructor for a clean, ID-free initialization.window object in your main entry point.pendingClass to show spinners during async validation.