Custom Validation (No Joi)
A login form using only custom validation functions — no external schema library required. Each field has its own validator function that returns an error object when validation fails.
What this example covers:
- Creating a form with
Alpine.Form()using inline validation functions - Writing custom validators with
config.validations - No external dependencies (no Joi, no Yup, etc.)
- Displaying field errors with
getFieldState()
Live Demo
JavaScript
Define validation functions directly in config.validations. Each function receives the field value and returns { message } on failure, or undefined on success.
js
Alpine.data('basicFormNoJoi', () => ({
result: '',
showResult: false,
form: Alpine.Form(
{},
{
config: {
validations: {
email(value) {
if (!value) return { message: 'Email is required' };
if (!/\S+@\S+\.\S+/.test(value))
return { message: 'Please enter a valid email' };
},
password(value) {
if (!value) return { message: 'Password is required' };
if (value.length < 6) return { message: 'Must be at least 6 characters' };
},
},
},
},
),
submitFunction(data) {
this.showResult = true;
this.result = JSON.stringify(Alpine.raw(data), null, 2);
},
}));HTML
The template is identical to the Joi example — the HTML doesn't care which validation approach is used.
html
<div x-data="basicFormNoJoi">
<form @submit.prevent="form.submit(submitFunction)">
<div class="field-group">
<label>Email</label>
<input x-register:form="form.field('email')" type="email" placeholder="Enter email" />
<span
class="field-error"
x-show="!form.getFieldState('email').isValid"
x-text="form.getFieldState('email').error"
></span>
</div>
<div class="field-group">
<label>Password</label>
<input
x-register:form="form.field('password')"
type="password"
placeholder="Enter password"
/>
<span
class="field-error"
x-show="!form.getFieldState('password').isValid"
x-text="form.getFieldState('password').error"
></span>
</div>
<div class="btn-group">
<button type="submit">Save</button>
<button type="button" @click="form.reset(); showResult = false;">Reset</button>
</div>
</form>
<pre x-show="showResult" x-text="result"></pre>
</div>Cross-Field Validation
Custom validators receive the full form data as the second argument, enabling cross-field checks:
js
validations: {
confirmPassword(value, data) {
if (value !== data.password) return { message: 'Passwords do not match' };
}
}