Alpine.js

Use x-validate directives and the validation() Alpine helper to run Laravel-style validation in reactive Blade forms.

Alpine.js is the shortest path from Laravel rule strings to interactive browser feedback. The package supports both field-level directives and a form-scoped validation() helper.

Register the adapter

import Alpine from 'alpinejs';
import registerAlpine from 'laravel-client-validation/alpine';

window.Alpine = Alpine;
registerAlpine(Alpine);
Alpine.start();

If you load the browser bundle through @clientValidationAssets, the package auto-registers Alpine integration when Alpine boots.

Validate individual fields

<input name="email" x-validate="'required|email'">
<input name="username" x-validate.live="'required|alpha_dash|min:3'">
<input type="password" name="password" x-validate.submit="'required|min:8|confirmed'">

x-validate validates on blur by default. .live adds debounced input validation and .submit blocks form submission until the field passes.

As long as at least one validated field lives inside a <form>, the adapter disables the browser's native constraint popup and installs a form-level guard: submitting the form re-validates every bound field in that form and blocks the submission until all of them pass. This is what makes blur/live fields participate when the form is saved — the .submit modifier just marks a field that should never be skipped.

Cross-field rules (confirmed, same:other, required_if:role,admin, …) work in bare-directive mode too: sibling values are read from the enclosing <form>, falling back to the surrounding x-data root. For full control over the compared state — or fields that live in different forms — use the validation() component instead, shown below.

Manage a full Alpine form

<div x-data="validation({
    rules: {
        email: 'required|email|unique:users,email',
        password: 'required|min:8',
        password_confirmation: 'required|same:password',
    },
    messages: {
        'password.min': 'Use at least eight characters.',
    },
})">
    <form @submit.prevent="submit(async (payload) => await fetch('/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(payload),
    }))">
        <input x-model="form.email" @input="validateLive('email')" @blur="validate('email')" name="email">
        <p x-show="hasError('email')" x-text="error('email')"></p>

        <input type="password" x-model="form.password" @blur="validate('password')" name="password">
        <p x-show="hasError('password')" x-text="error('password')"></p>
    </form>
</div>

The helper keeps touched state, per-field errors, and debounced remote validation in one Alpine object. Use stateClass(field) when you want to map the package validity state into your own utility classes.

Cross-field and remote rules

Rules such as same, different, required_if, and unique work best when every sibling value lives in the same form object so Alpine always passes the latest state into the validator. Bare directives also compare against sibling inputs in the same <form> automatically; prefer the component form when fields span multiple forms or you keep values outside the DOM.

  • Use installation to bootstrap the bundle in Blade layouts.
  • Keep usage nearby for the cross-framework model.
  • Open examples when you need larger Blade snippets.