Validation functionality can be enabled for all form components using the validation plugin. Once included, from CDN or as a module, you can use the form
component and x-validation
directive to validate inputs based on a set of rules and display validation messages to users.
<div x-data="form" data-form-name="username">
<div
x-data="input"
data-placeholder="Username"
x-validation:input='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ]}'
class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div x-data="formText" data-input="input" class="flex flex-col">
<template x-for="m in getMessages()">
<div
x-bind="message"
x-text="m"
class="text-sm"
class-valid="text-success-600 dark:text-success-500"
class-invalid="text-danger-600 dark:text-danger-500"
></div>
</template>
</div>
</div>
<script defer src="https://cdn.jsdelivr.net/npm/litewind-alpine@0.x.x/plugins/validation/dist/cdn.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/litewind-alpine@0.x.x/components/form-text/dist/cdn.min.js"></script>
The main part of validation is the form
component that is used to store validation data. The validation rules and other options are set seperately for each input in the form
with the x-validation
directive. Third part of the validation plugin is the $validation
utility magic function.
The form-text
is a separate, optional component that can be used to display validation messages.
Form component is a container for validated inputs that stores validation results and other validation related data. Form component has one required data-form-name
prop. It also provides validateForm
function that validates all inputs inside form and resetForm
function that resets validation results to default state. If all inputs inside form are valid then valid
property of the form component is set to true.
The x-validation
directive is used to set the rules and the mode of the validation for the input in the form
. The value of the directive is in JSON format and has following properties:
rules
- is an array of objects and strings. Simple rules use strings and rules that require argument use objects.mode
- is a string that defines when to validate input and how to update state depending on the validation results. If mode is not defined the default "blur-silent"
is used.messages
- is an object containing validation messages. If messages are not defined the default global messages are used.Available rules include following:
required
alpha
numeric
alphanumeric
minLength
maxLength
minElements
maxElements
minValue
maxValue
email
atLeastOneUppercase
atLeastOneLowercase
atLeastOneDigit
atLeastOneSpecial
sameAs
The mode defines when to validate input and how to update the state based on the validation results. Available modes are:
"blur-silent"
- validates when the input loses focus; updates state only for invalid inputs."blur-eager"
- validates when the input loses focus; updates state for both valid and invalid inputs."form-silent"
- validates when the formValidate
function is called; updates state only for invalid inputs."form-eager"
- validates when the formValidate
function is called; update state for both valid and invalid inputs."immediate-eager"
- validate on each input update; updates state for both valid and invalid inputs.<div
x-data="form"
data-form-name="validation-mode"
class="flex flex-col gap-y-4"
>
<div>
<span class="font-mono font-semibold"> Mode: "blur-silent" </span>
<div
x-data="input"
data-placeholder="Username"
x-validation:input-blur-silent='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ], "mode": "blur-silent"}'
class="mt-2 flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div x-data="formText" data-input="input-blur-silent" class="flex flex-col">
<template x-for="m in getMessages()">
<div
x-bind="message"
x-text="m"
class="text-sm"
class-valid="text-success-600 dark:text-success-500"
class-invalid="text-danger-600 dark:text-danger-500"
></div>
</template>
</div>
</div>
<hr class="my-6" />
<div>
<span class="font-mono font-semibold"> Mode: "blur-eager" </span>
<div
x-data="input"
data-placeholder="Username"
x-validation:input-blur-eager='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ], "mode": "blur-eager"}'
class="mt-2 flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div x-data="formText" data-input="input-blur-eager" class="flex flex-col">
<template x-for="m in getMessages()">
<div
x-bind="message"
x-text="m"
class="text-sm"
class-valid="text-success-600 dark:text-success-500"
class-invalid="text-danger-600 dark:text-danger-500"
></div>
</template>
</div>
</div>
<hr class="my-6" />
<div>
<span class="font-mono font-semibold"> Mode: "immediate-eager" </span>
<div
x-data="input"
data-placeholder="Username"
x-validation:input-immediate-eager='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ], "mode": "immediate-eager"}'
class="mt-2 flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div
x-data="formText"
data-input="input-immediate-eager"
class="flex flex-col"
>
<template x-for="m in getMessages()">
<div
x-bind="message"
x-text="m"
class="text-sm"
class-valid="text-success-600 dark:text-success-500"
class-invalid="text-danger-600 dark:text-danger-500"
></div>
</template>
</div>
</div>
<hr class="my-6" />
<div>
<span class="font-mono font-semibold"> Mode: "form-silent" </span>
<div
x-data="input"
data-placeholder="Username"
x-validation:input-form-silent='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ], "mode": "form-silent"}'
class="mt-2 flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div x-data="formText" data-input="input-form-silent" class="flex flex-col">
<template x-for="m in getMessages()">
<div
x-bind="message"
x-text="m"
class="text-sm"
class-valid="text-success-600 dark:text-success-500"
class-invalid="text-danger-600 dark:text-danger-500"
></div>
</template>
</div>
<button
@click="$validation('input-form-silent').formValidate()"
class="mt-4 ml-auto block flex items-center rounded-md border-violet-700 bg-violet-500 px-4 py-2 font-medium text-gray-100 hover:bg-violet-600 focus:ring-violet-200 dark:bg-violet-500 dark:hover:bg-violet-600"
>
Validate
</button>
</div>
<hr class="my-6" />
<div>
<span class="font-mono font-semibold"> Mode: "form-eager" </span>
<div
x-data="input"
data-placeholder="Username"
x-validation:input-form-eager='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ], "mode": "form-eager"}'
class="mt-2 flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div x-data="formText" data-input="input-form-eager" class="flex flex-col">
<template x-for="m in getMessages()">
<div
x-bind="message"
x-text="m"
class="text-sm"
class-valid="text-success-600 dark:text-success-500"
class-invalid="text-danger-600 dark:text-danger-500"
></div>
</template>
</div>
<button
@click="$validation('input-form-eager').formValidate()"
class="mt-4 ml-auto block flex items-center rounded-md border-violet-700 bg-violet-500 px-4 py-2 font-medium text-gray-100 hover:bg-violet-600 focus:ring-violet-200 dark:bg-violet-500 dark:hover:bg-violet-600"
>
Validate
</button>
</div>
</div>
Each rule has default generic validation message for the invalid value. You can customize these messages for the input by adding them to the messages
property. The key is a rule name and the value is the new message. The message can contain special %d
characters that are replaced with a rule value.
Validation results for each input are stored in the form
component. This data is automatically used by inputs and other components to set validation related styles and validation messages. Validation results can also be accessed with the $validation
magic function that takes input name as and argument and returns validation results object.
The validation result is an object with following propreties:
status
- object containg the results of validation and the current state of the input (for example touched
, dirty
etc). It is updated once initially and then after each value change.state
- final validation state of input. This state is based on the current status
and is updated only when conditions for the mode
are fulfilled (for example input lost focus etc). By default it is empty string for initial state of input, "valid"
for valid input and "invalid"
for invalid input.messages
- object containing validation messages.See how these properties are updated depending on the input value in the next example.
<div x-data="form" data-form-name="username" class="flex items-start gap-x-10">
<div
x-data="input"
data-placeholder="Username"
x-validation:input='{ "rules": [{ "minLength": 3 }, { "maxLength": 12 }, "required", "alphanumeric" ]}'
class="flex flex-1 items-center rounded-sm border px-3 py-2 outline-hidden transition-shadow duration-200 focus-within:ring-3 focus:outline-hidden"
class-default="border-gray-300 bg-white focus-within:border-gray-400 focus-within:ring-primary-200 dark:border-dark-600 dark:bg-dark-800 dark:text-text-300 dark:focus-within:ring-primary-300"
class-valid="border-success-300 bg-white text-success-600 focus-within:ring-success-200 dark:border-success-400 dark:bg-dark-800 dark:text-success-600 dark:focus-within:ring-success-300"
class-invalid="border-danger-300 bg-white text-danger-600 focus-within:ring-danger-200 dark:border-danger-400 dark:bg-dark-800 dark:text-danger-600 dark:focus-within:ring-danger-300"
>
<div data-icon class="mr-3 empty:hidden"></div>
<div data-prepend class="mr-3 empty:hidden"></div>
<div class="mr-3 flex flex-1 flex-wrap">
<input
x-bind="input"
type="text"
class="w-full min-w-0 flex-1 border-0 bg-transparent p-0 outline-hidden focus:min-w-[64px] focus:outline-hidden"
/>
</div>
<div data-append class="mr-3 empty:hidden"></div>
<div class="flex items-center gap-x-2">
<div x-bind="loader">
<svg
viewBox="25 25 50 50"
fill="none"
class="h-5 w-5 animate-spinner-rotate"
>
<circle
cx="50"
cy="50"
r="20"
stroke="currentColor"
stroke-width="4"
stroke-miterlimit="10"
stroke-linecap="round"
class="animate-spinner-dash"
/>
</svg>
</div>
<button x-bind="clearButton" @click="clear()" class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
fill="currentColor"
class="h-5 w-5 opacity-70"
viewBox="0 0 16 16"
>
<path
d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"
/>
</svg>
</button>
</div>
</div>
<div class="flex w-1/2 flex-col gap-y-4 text-sm">
<pre
x-text="'status: ' + JSON.stringify($validation('input').status, null, 2)"
></pre>
<pre
x-text="'messages: ' + JSON.stringify($validation('input').messages, null, 2)"
></pre>
<pre x-text="'state: ' + $validation('input').state"></pre>
</div>
</div>
For the CDN build of the plugin you can add new rules to the window.Litewind.globalValidators
object. A rule is simply a function that takes tested value as an argument and returns true
or a message.
For a module build of the plugin import globalValidators
object from the plugin file.
For the CDN build of the plugin you can modify default messages for the rules in the window.Litewind.validationMessages
object.
For a module build of the plugin import validationMessages
object from the plugin file.
A message can contain %d
charecters that are replaced with a rule value.