Very simple form validation plugin for AlpineJS. This plugin is designed, like AlpineJS itself, to be sprinkled in as needed. What this plugin does not do is impose an opinionated structure for your form data or functionality. It can be used with or without x-model.
The x-validate directive allows for simple validation and error display. It also captures all form data in a reactive formData. The $validate magic function grants access to validation functions, formData, and simple submit validation check.
:disabled
will work.:value
or other javascript that dynamically updates the value. The mutation observer automatically spots the change and updates.:required
instead to set the required dynamically.Add a x-data
, and x-validate
to your <form>
element (you don't need any variables on x-data ; it just needs to be initiated as an Alpine component). This automatically:
required
attribute and input types.span.error-msg
with error message adjacent to the field.
aria-invalid="true"
and aria-errormessage
attributes to the field element.hidden
attribute.data-error="{error message}"
on the field's parent element for any additional styling you may want (searches for closest field-parent
class parent element or failing that uses .parentNode
).:disabled="$validate.isComplete('formId')"
to disable submit button and/or use @submit="$validate.submit"
to automatically check validity of all fields prior to submitting.x-validate
directive along with modifiers directly on form fields to add additional validation, such as x-validate.wholenumber
data-error-msg='custom error message'
on field itselferror-msg
class and write your own error message there. The plugin searches all next siblings and will add the proper aria-errormessage with linked id tags for you. This is also useful if you want some descriptive text or other element before the error message or if you want to use special styling to a specific error message.error-msg-${id of matching field}
) and it will use that.x-validate.group
on groups of checkboxes or radio buttons to validate that at least one is selected.x-validate='$el.value === 'bunny'
(see Using Expressions for Validation below).$validate.isComplete(el)
to detect if the form, <fieldset>
groups or any field is completedx-validate
on the <form>
and just add x-validate
on fields directly if you only want a couple fields validated. The x-data is still required on <form>
.The UI modifiers are mainly for setting global defaults on <form>
but you can also be used on individual form field elements for more specific control.
x-validate
triggers validation and captures formData for entire formx-validate.bluronly
— change the default never to use 'input' event listener on fillable form fieldsx-validate.input
sets fields to use both 'blur' and 'input' event listener for all validationx-validate.refocus
sets fields to force focus on form element when invalidx-validate.use-browser
use the browser's built in validation as well as x-validatex-validate.validate-on-submit
automatically trigger validation on submitThese are bonus directives for built in regex validation. You can also ignore these and use the pattern
attribute with your own regex.
Used on <input>
, <select>
, <textarea>
x-validate
— only captures data; Useful on it's own if not set on <form>
. Also useful with paired with an expression for specific field validation like x-validate='$el.value === 'bunny'
. _Note that in many cases the normal pattern attribute may be all you need._x-validate.required
— replacement for required
attribute *x-validate.tel
- works the same as type='tel' using improved regex *x-validate.email
- works the same as type='email' using improved regex *x-validate.website
— valid if site domain, with or without http:// or https://x-validate.url
— works the same as type='url' using improved regex *x-validate.number
— valid if number (positive or negative; integer or decimal)x-validate.integer
— valid if integer number (positive or negative)x-validate.wholenumber
— valid if whole number (positive integer)x-validate.date
defaults to 'yyyy-mm-dd' format, as that is what type='date'
input field saves the date asx-validate.date.mmddyyyy
— 'mm-dd-yyyy' formatx-validate.date.ddmmyyyy
— 'dd-mm-yyyy' formatx-validate.date.yyyymmdd
— 'yyyy-mm-dd' format* this allows you to use x-validate.required
instead of required
attribute or x-validate.tel
on type='text'
instead of type='tel'
You can add a specific test to a field like x-validate='$el.value === 'bunny'
; this can be paired up with other validations. For example: x-validate.website='$el.includes('bunny')
for only websites with the word bunny in the name. _Note that in many cases the normal pattern attribute may be all you need._
Checkboxes and radio buttons that share the same name attribute update the same formData field data object. Radio buttons update the value with the currently selected button. Checkboxes save as both a comma separated string value and as an array.
You can validate that at least one is selected by adding x-validate.group
to every checkbox or radio button in a named group. If you want the user to select multiple checkboxes, use an added expression with the minimum number x-validate.group="2"
.
Note: Checkbox and radio button groups add their error message after the wrapper for the group. This is either the closest field-parent
class parent element or it uses .parentNode.parentNode
by default. It's assumed that each checkbox/radio is wrapped in a label or list item, and then has a wrapper around the group.
$formData
returns the formData object for the current form$validate
is an object with a group of functions (see functions below)You can add any specific validation like email or tel. Main difference between the magic function and the directive is that required is assumed.
$validate.email('')
and $validate.email('hi')
returns false$validate.email('hi@hello.com')
returns trueval === '' || $validate.email(val)
returns true if val is empty string or is a valid email address$validate.date.mmddyyyy(str)
When used on <form>
, the x-validate
every field is added to a reactive formData[formId] array. If only used on individual fields, x-validate
only adds those fields to the formData[formId] array.
@submit="$validate.submit"
used on form element. Validates current form; if validation fails errors are shown and the submit event is prevented.$validate.isComplete(el)
returns true or false validity for form, fieldsets, or fields. *$validate.data(el)
returns an array of form or fieldset fields, or an individual field data object *$validate.formData(form)
returns the raw formData object with keyed field names *$validate.value(el,value)
returns a simple object with field names keys and their values for forms and fieldsets or just the raw value for fields. Value argument is optional and only for fields; when provided it will update the field value and update the x-validate formData. ** 'el' argument variable works with either $refs or a string of the name/id for getting data from form, fieldset, and fields.
These grant access to some of the backend functions — use at your own risk.
$validate.updateData(field,data,triggerErrorMsg)
allows you to directly add/update the formData array for a field. When called with just the field name it will grab the new value for the field. This is useful if you are updating the field dynamically via other javascript functions.$validate.toggleError(field,valid)
allows you to toggle the error message on any field.{
'field-name': {
name: 'field-name', node: [field HTMLElement], value: 'field value', valid: true, required: true, mods: [array of directive modifiers], set: [parent fieldset HTMLElement], parentNode: [parent HTMLElement], array: [array of checked selections (only used groups of checkboxes or radio buttons)], exp: [expression on x-validate]
}
}
Note: name = name attribute || id attribute
More complicated examples in examples folder. run npm run serve
to view.
<form id="form" x-data x-validate.validate-on-submit action="/api/end-point" method="post">
<p><em>* required</em></p>
<div>
<label for="name">Your Name *</label>
<input type="text" id="name" name="name" required />
</div>
<div>
<label for="email">Your Email *</label>
<input type="email" id="email" name="email" required />
</div>
<div>
<label for="wholenumber">Whole Number</label>
<input type="wholenumber" id="wholenumber" name="wholenumber" x-validate.wholenumber data-error-msg="positive whole number required" />
</div>
<div id="animals" data-error-msg="you must pick at least one animal">
<h4>Favorite Animals *</h4>
<label><input type="checkbox" x-validate.group name="animal" id="cat" value="cat" />
Cat</label>
<label><input type="checkbox" x-validate.group name="animal" id="dog" value="dog" />
Dog</label>
<label><input type="checkbox" x-validate.group name="animal" id="bunny" value="bunny" />
Bunny</label>
</div>
<div>
<input type="submit" value="submit">
</div>
</form>
<style type="text/css">
/* style to display the error message */
.error-msg {
color: red;
}
</style>
The above example will validate using x-validate prior to submitting. If you need to submit using javascript, then you can use alpine for this as such:
<form id="form" x-data x-validate.validate-on-submit
@submit.prevent="if ($validate.isComplete('form')) yourPostFormFunctionHere($validate.value('form'))" >
.error-msg[hidden] {
opacity: 0;
height: 0px;
transform: scale(0);
}
.error-msg {
font-size: 0.8rem;
font-weight: 700;
color: darkred;
transform-origin: left;
transition: all 200ms;
display: block;
}
<script defer src="https://unpkg.com/@colinaut/alpinejs-plugin-simple-validate@1/dist/alpine.validate.min.js"></script>
<script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
npm i @colinaut/alpinejs-plugin-simple-validate
pnpm i @colinaut/alpinejs-plugin-simple-validate
yarn add @colinaut/alpinejs-plugin-simple-validate
If you are bundling your javascript then you can initialize the plugin like so:
import Alpine from "alpinejs";
import validate from "@colinaut/alpinejs-plugin-simple-validate";
Alpine.plugin(validate);
window.Alpine = Alpine;
Alpine.start();
If you are using Eleventy, and want to install locally rather than rely on the CDN, you can install via NPM/PNPM/YARN and then pass through the js file so that it is included in the output. Then you would just need to add it to the head.
eleventyConfig.addPassthroughCopy({
"node_modules/alpinejs/dist/cdn.min.js" : "js/alpine.min.js",
"node_modules/@colinaut/alpinejs-plugin-simple-validate/dist/alpine.validate.min.js": "js/alpine.validate.min.js",
})
<script src="https://github.com/colinaut/alpinejs-plugin-simple-validate/raw/main/js/alpine.validate.min.js" defer></script>
<script src="https://github.com/colinaut/alpinejs-plugin-simple-validate/raw/main/js/alpine.min.js" defer></script>
Feel free to add any enhancement requests on github.
Built using AlpineJS plugin blueprint