Basic example
TW elements Validation allows you to show the result of you validation in a simple and elegant way. You can use just the styling that Validation
component provides or you can add rules and messages for each input. Validation
component is fully customizable.
If you want to control the validation yourself and only use the styling, add data-twe-validated="true"
to the form and data-twe-validation-state="valid/invalid"
to the element if it's value is valid.
To initialize the component use data-twe-validation-init
attribute and add data-twe-validate="input"
to the wrapper of element you want the styles to be applied to.
Inside the data-twe-validate
attribute you can provide
-
input
- for TW Elements input components basic
- for native inputscheckbox
- for checkboxes and switchesradio
- radio component
<form id="form-1" data-twe-validation-init> <div class="relative mb-3" id="input-1" data-twe-input-wrapper-init data-twe-validate="input"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleFormControlInput1" placeholder="Example label" /> <label for="exampleFormControlInput1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example label </label> </div> <button type="button" id="validation-1" class="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"> Validate </button> </form>
import { Validation, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Input }); const validation1 = document.getElementById("validation-1"); const form1 = document.getElementById("form-1"); const input1 = document.getElementById("input-1"); let valid = true; validation1.addEventListener("click", (e) => { e.preventDefault(); input1.setAttribute( "data-twe-validation-state", valid ? "valid" : "invalid" ); form1.setAttribute("data-twe-validated", true); valid = !valid; });
const validation1 = document.getElementById("validation-1"); const form1 = document.getElementById("form-1"); const input1 = document.getElementById("input-1"); let valid = true; validation1.addEventListener("click", (e) => { e.preventDefault(); input1.setAttribute( "data-twe-validation-state", valid ? "valid" : "invalid" ); form1.setAttribute("data-twe-validated", true); valid = !valid; });
Advanced Example
Add data-twe-validation-ruleset
attribute with a |
separated list of validation rules to make the component check them for you. Add data-twe-submit-btn-ref
to the submit button so that you don't have to listen on the button clicks yourself.
Click the Validate
button to validate the form again.
<form data-twe-validation-init> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleFormAdvancedInput1" placeholder="Example label" /> <label for="exampleFormAdvancedInput1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example label </label> </div> <button type="button" class="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Input });
Existing validation rules
In TW elements validation we have already provided few rules that can be used to check the value of inputs. You can extend the list with use of customRules
and customErrorMessages
options. Example on how to do this can be found in section Custom Validation
isRequired
- checks if any value was setisEmail
- checks for valid email address-
isLongerThan
- checks if string is longer than (n) length -
isShorterThan
- checks if string is shorter than (n) length isChecked
- checks if value was checked-
isPhone
- checks if the value has length equal to 9 isNumber
- checks if value is type numberisString
- checks if value is type string-
isBoolean
- checks if value is type boolean -
isDate
- checks if value is a date in format DD/MM/YYYY -
is12hFormat
- checks if value is time in 12h format -
is24hFormat
- checks if value is time in 24h format
Supported components
The Validation
component supports:
Select
Used rules: isRequired
<form data-twe-validation-init> <div data-twe-validate="input" data-twe-validation-ruleset="isRequired"> <select data-twe-select-init data-twe-select-clear-button="true"> <option value="" hidden selected></option> <option value="1">One</option> <option value="2">Two</option> <option value="3">Three</option> <option value="4">Four</option> <option value="5">Five</option> <option value="6">Six</option> <option value="7">Seven</option> <option value="8">Eight</option> </select> <label data-twe-select-label-ref>Example label</label> </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Select, initTWE, } from "tw-elements"; initTWE({ Validation, Select });
Datepicker
Used rules: isRequired, isDate
<form data-twe-validation-init> <div class="relative mb-3" data-twe-datepicker-init data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isDate"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" placeholder="Select a date" /> <label for="floatingInput" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Select a date</label > </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Datepicker, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Datepicker, Input });
Timepicker
Used rules: isRequired, is12hFormat
<form data-twe-validation-init> <div class="relative mb-3" data-twe-timepicker-init data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|is12hFormat"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="form1" /> <label for="form1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Select a time</label > </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Timepicker, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Timepicker, Input });
Input Group
With default input. Used rules: isRequired
<form data-twe-validation-init> <div class="relative flex flex-nowrap items-start"> <span class="flex items-center whitespace-nowrap rounded-s border border-e-0 border-solid border-neutral-200 px-3 py-[0.25rem] text-center text-base font-normal leading-[1.6] text-surface dark:border-white/10 dark:text-white" id="addon-wrapping" >@</span > <div class="relative inline-block w-full" data-twe-validate="basic" data-twe-validation-ruleset="isRequired"> <input type="text" class="relative m-0 block w-full flex-auto rounded-e border border-solid border-neutral-200 bg-transparent bg-clip-padding px-3 py-[0.25rem] text-base font-normal leading-[1.6] text-surface outline-none transition duration-200 ease-in-out placeholder:text-neutral-500 focus:z-[3] focus:border-primary focus:shadow-inset focus:outline-none motion-reduce:transition-none dark:border-white/10 dark:text-white dark:placeholder:text-neutral-200 dark:autofill:shadow-autofill dark:focus:border-primary" placeholder="Username" aria-label="Username" aria-describedby="addon-wrapping" /> </div> </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, initTWE, } from "tw-elements"; initTWE({ Validation });
With TE Input. Used rules: isRequired
<form data-twe-validation-init> <div class="relative mb-4 flex items-stretch" data-twe-input-wrapper-init data-twe-input-group-ref data-twe-validate="input" data-twe-validation-ruleset="isRequired"> <span class="flex items-center whitespace-nowrap border-e border-solid border-secondary-500 px-3 py-[0.25rem] text-center text-base font-normal leading-[1.6] text-surface dark:border-neutral-400 dark:text-white" id="basic-addon1" data-twe-input-group-text-ref >@</span > <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" placeholder="Username" aria-label="Username" id="exampleFormControlInput" aria-describedby="basic-addon1" /> <label for="exampleFormControlInput" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example label </label> </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Input });
Textarea
Used rules: isRequired
<form data-twe-validation-init> <div class="relative mb-4 flex items-stretch" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired"> <textarea class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleFormControlTextarea1" rows="3" placeholder="Your message"></textarea> <label for="exampleFormControlTextarea1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example textarea</label > </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Input });
Checkbox
Used rules: isChecked
<form data-twe-validation-init> <div class="relative block min-h-[1.5rem] ps-[1.5rem]" data-twe-validate="checkbox" data-twe-validation-ruleset="isChecked"> <input class="relative float-left -ms-[1.5rem] me-[6px] mt-[0.15rem] h-[1.125rem] w-[1.125rem] appearance-none rounded-[0.25rem] border-[0.125rem] border-solid border-secondary-500 outline-none before:pointer-events-none before:absolute before:h-[0.875rem] before:w-[0.875rem] before:scale-0 before:rounded-full before:bg-transparent before:opacity-0 before:shadow-checkbox before:shadow-transparent before:content-[''] checked:border-primary checked:bg-primary checked:before:opacity-[0.16] checked:after:absolute checked:after:-mt-px checked:after:ms-[0.25rem] checked:after:block checked:after:h-[0.8125rem] checked:after:w-[0.375rem] checked:after:rotate-45 checked:after:border-[0.125rem] checked:after:border-l-0 checked:after:border-t-0 checked:after:border-solid checked:after:border-white checked:after:bg-transparent checked:after:content-[''] hover:cursor-pointer hover:before:opacity-[0.04] hover:before:shadow-black/60 focus:shadow-none focus:transition-[border-color_0.2s] focus:before:scale-100 focus:before:opacity-[0.12] focus:before:shadow-black/60 focus:before:transition-[box-shadow_0.2s,transform_0.2s] focus:after:absolute focus:after:z-[1] focus:after:block focus:after:h-[0.875rem] focus:after:w-[0.875rem] focus:after:rounded-[0.125rem] focus:after:content-[''] checked:focus:before:scale-100 checked:focus:before:shadow-checkbox checked:focus:before:transition-[box-shadow_0.2s,transform_0.2s] checked:focus:after:-mt-px checked:focus:after:ms-[0.25rem] checked:focus:after:h-[0.8125rem] checked:focus:after:w-[0.375rem] checked:focus:after:rotate-45 checked:focus:after:rounded-none checked:focus:after:border-[0.125rem] checked:focus:after:border-l-0 checked:focus:after:border-t-0 checked:focus:after:border-solid checked:focus:after:border-white checked:focus:after:bg-transparent rtl:float-right dark:border-neutral-400 dark:checked:border-primary dark:checked:bg-primary" type="checkbox" value="" id="checkboxChecked" checked /> <label class="inline-block ps-[0.15rem] hover:cursor-pointer" for="checkboxChecked"> Checked checkbox </label> </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, initTWE, } from "tw-elements"; initTWE({ Validation });
Switch
Used rules: isChecked
<form data-twe-validation-init> <div class="relative block min-h-[1.5rem]" data-twe-validate="checkbox" data-twe-validation-ruleset="isChecked"> <input class="me-2 mt-[0.3rem] h-3.5 w-8 appearance-none rounded-[0.4375rem] bg-black/25 before:pointer-events-none before:absolute before:h-3.5 before:w-3.5 before:rounded-full before:bg-transparent before:content-[''] after:absolute after:z-[2] after:-mt-[0.1875rem] after:h-5 after:w-5 after:rounded-full after:border-none after:bg-white after:shadow-switch-2 after:transition-[background-color_0.2s,transform_0.2s] after:content-[''] checked:bg-primary checked:after:absolute checked:after:z-[2] checked:after:-mt-[3px] checked:after:ms-[1.0625rem] checked:after:h-5 checked:after:w-5 checked:after:rounded-full checked:after:border-none checked:after:bg-primary checked:after:shadow-switch-1 checked:after:transition-[background-color_0.2s,transform_0.2s] checked:after:content-[''] hover:cursor-pointer focus:outline-none focus:before:scale-100 focus:before:opacity-[0.12] focus:before:shadow-switch-3 focus:before:shadow-black/60 focus:before:transition-[box-shadow_0.2s,transform_0.2s] focus:after:absolute focus:after:z-[1] focus:after:block focus:after:h-5 focus:after:w-5 focus:after:rounded-full focus:after:content-[''] checked:focus:border-primary checked:focus:bg-primary checked:focus:before:ms-[1.0625rem] checked:focus:before:scale-100 checked:focus:before:shadow-switch-3 checked:focus:before:transition-[box-shadow_0.2s,transform_0.2s] dark:bg-white/25 dark:after:bg-surface-dark dark:checked:bg-primary dark:checked:after:bg-primary" type="checkbox" role="switch" id="flexSwitchCheckDefault" /> <label class="inline-block ps-[0.15rem] hover:cursor-pointer" for="flexSwitchCheckDefault" >Default switch checkbox input</label > </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, initTWE, } from "tw-elements"; initTWE({ Validation });
Radio
Used rules: isChecked
<form data-twe-validation-init> <div class="relative block min-h-[1.5rem] ps-[1.5rem]" data-twe-validate="radio" data-twe-validation-ruleset="isChecked"> <input class="relative float-left -ms-[1.5rem] me-1 mt-0.5 h-5 w-5 appearance-none rounded-full border-2 border-solid border-secondary-500 before:pointer-events-none before:absolute before:h-4 before:w-4 before:scale-0 before:rounded-full before:bg-transparent before:opacity-0 before:shadow-checkbox before:shadow-transparent before:content-[''] after:absolute after:z-[1] after:block after:h-4 after:w-4 after:rounded-full after:content-[''] checked:border-primary checked:before:opacity-[0.16] checked:after:absolute checked:after:left-1/2 checked:after:top-1/2 checked:after:h-[0.625rem] checked:after:w-[0.625rem] checked:after:rounded-full checked:after:border-primary checked:after:bg-primary checked:after:content-[''] checked:after:[transform:translate(-50%,-50%)] hover:cursor-pointer hover:before:opacity-[0.04] hover:before:shadow-black/60 focus:shadow-none focus:outline-none focus:ring-0 focus:before:scale-100 focus:before:opacity-[0.12] focus:before:shadow-black/60 focus:before:transition-[box-shadow_0.2s,transform_0.2s] checked:focus:border-primary checked:focus:before:scale-100 checked:focus:before:shadow-checkbox checked:focus:before:transition-[box-shadow_0.2s,transform_0.2s] rtl:float-right dark:border-neutral-400 dark:checked:border-primary" type="radio" name="flexRadioDefault" id="radioDefault01" /> <label class="mt-px inline-block ps-[0.15rem] hover:cursor-pointer" for="radioDefault01"> Default radio </label> </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, initTWE, } from "tw-elements"; initTWE({ Validation });
Active validation
Add data-twe-active-validation="true"
to make the validation listen to the input
changes.
<form data-twe-validation-init data-twe-active-validation="true" autocomplete="off"> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isNumber|isLongerThan(2)|isShorterThan(6)"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleActive1" placeholder="Example label" /> <label for="exampleActive1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example label </label> </div> <button type="button" class="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
// Initialization for ES Users import { Validation, Input, initTWE, } from "tw-elements"; initTWE({ Validation, Input });
Custom validation
You can add custom rules to the validation. Simply add a new method to the customRules
option and add customErrorMessages
for that method. You can also add a callback to the validation so that the Validation
component will call it after the validation is done. Check the inspect tools to see how it works.
<form id="form-0" data-twe-active-validation="true"> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isNumber|isLongerThan(2)"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleInput1" placeholder="Example label" /> <label for="exampleInput1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example label </label> </div> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isEmail|contains(test)"> <input type="text" name="email" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleInput2" placeholder="Example label" /> <label for="exampleInput2" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Example label </label> </div> <div class="relative mb-3" data-twe-datepicker-init data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isDate"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" placeholder="Select a date" /> <label class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Select a date</label > </div> <div class="relative mb-3" data-twe-timepicker-init data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|is12hFormat"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleInput3" /> <label for="exampleInput3" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Select a time</label > </div> <button type="button" class="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> <button class="inline-block rounded px-2 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-danger hover:text-danger-600 focus:text-danger-600 focus:outline-none focus:ring-0 active:text-danger-700" id="dispose"> Dispose validation </button> </form>
import { Validation, Input, Datepicker, Timepicker, initTWE, } from "tw-elements"; initTWE({ Input, Datepicker, Timepicker }); const fullValidationDisposeBtn = document.getElementById("dispose"); const fullValidationForm = document.getElementById("form-0"); const fullValidation = new Validation(fullValidationForm, { customErrorMessages: { contains: "The field must contain the '{contains}' word", }, customRules: { contains: (value, message, string) => { return value.includes(string) ? true : message.replace("{contains}", string); }, }, submitCallback: (e, valid) => { console.log("Do something ...", "Validation passed: ", valid); }, }); fullValidationDisposeBtn.addEventListener("click", (e) => { e.preventDefault(); fullValidation.dispose(); }); fullValidationForm.addEventListener("invalid.twe.validation", (e) => { console.log("Something went wrong!"); }); fullValidationForm.addEventListener("valid.twe.validation", (e) => { console.log("All good!"); });
const fullValidationDisposeBtn = document.getElementById("dispose"); const fullValidationForm = document.getElementById("form-0"); const fullValidation = new twe.Validation(fullValidationForm, { customErrorMessages: { contains: "The field must contain the '{contains}' word", }, customRules: { contains: (value, message, string) => { return value.includes(string) ? true : message.replace("{contains}", string); }, }, submitCallback: (e, valid) => { console.log("Do something ...", "Validation passed: ", valid); }, }); fullValidationDisposeBtn.addEventListener("click", (e) => { e.preventDefault(); fullValidation.dispose(); }); fullValidationForm.addEventListener("invalid.twe.validation", (e) => { console.log("Something went wrong!"); }); fullValidationForm.addEventListener("valid.twe.validation", (e) => { console.log("All good!"); });
Validation with tooltips
Use emitted events to show tooltips on validation results.
<form id="tooltips" class="me-10" data-twe-active-validation="true"> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isLongerThan(4)"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0 dark:autofill:shadow-autofill" name="name" /> <label class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Full name </label> <span class="absolute -end-8 top-1/2 -translate-y-1/2 cursor-pointer text-neutral-500 dark:text-neutral-200 [&>svg]:h-5 [&>svg]:w-5" id="fullNameInfo"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" /> </svg> </span> </div> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isEmail"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0 dark:autofill:shadow-autofill" name="email" /> <label class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Email address </label> <span class="absolute -end-8 top-1/2 -translate-y-1/2 cursor-pointer text-neutral-500 dark:text-neutral-200 [&>svg]:h-5 [&>svg]:w-5" id="emailInfo"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" /> </svg> </span> </div> <div class="relative mb-4 flex items-stretch" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isLongerThan(10)"> <textarea class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" rows="3" name="issue"></textarea> <label class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Describe your issue</label > <span class="absolute -end-8 top-1/2 -translate-y-1/2 cursor-pointer text-neutral-500 dark:text-neutral-200 [&>svg]:h-5 [&>svg]:w-5" id="issueInfo"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z" /> </svg> </span> </div> <button type="button" class="mt-2 inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form>
import { Validation, Input, Tooltip, initTWE, } from "tw-elements"; initTWE({ Input }); const tooltipsForm = document.getElementById("tooltips"); const tooltipsInfo = [ { element: document.getElementById("fullNameInfo"), name: "name", message: { isRequired: "This value is required", isLongerThan: "Must be longer than 4 characters", }, }, { element: document.getElementById("emailInfo"), name: "email", message: { isRequired: "This value is required", isEmail: "Must be a valid email address", }, }, { element: document.getElementById("issueInfo"), name: "issue", message: { isRequired: "This value is required", isLongerThan: "Must be longer than 10 characters", }, }, ]; const generateMessage = (result, message) => `<p class="${result ? 'text-success' : 'text-danger'}">${message}</p>`; tooltipsInfo.forEach((tooltip) => { const title = Object.entries(tooltip.message) .map(([key, value]) => generateMessage(false, value)) .join(""); tooltip.instance = new Tooltip(tooltip.element, { html: true, template: ` <div> <div class="bg-white text-xs p-4 ms-4 rounded border dark:bg-body-dark dark:border-black/40" data-twe-tooltip-inner-ref></div> </div> `, placement: "right", title, }); }); const tooltipsValidation = new Validation(tooltipsForm); tooltipsForm.addEventListener("valueChanged.twe.validation", (e) => { const element = tooltipsInfo.find( (tooltip) => tooltip.name === e.value.name ); element.instance.dispose(); const title = Object.entries(element.message) .map(([key, value]) => { const result = e.value.validation.find( (res) => key === res.name ).result; return generateMessage(result, value); }) .join(""); const borderColor = e.value.result === "valid" ? "border-success" : "border-danger"; element.instance = new Tooltip(element.element, { html: true, template: ` <div> <div class="bg-white text-xs p-4 ms-4 rounded border dark:bg-body-dark dark:border-black/40" data-twe-tooltip-inner-ref></div> </div> `, placement: "right", title: title, }); });
const tooltipsForm = document.getElementById("tooltips"); const tooltipsInfo = [ { element: document.getElementById("fullNameInfo"), name: "name", message: { isRequired: "This value is required", isLongerThan: "Must be longer than 4 characters", }, }, { element: document.getElementById("emailInfo"), name: "email", message: { isRequired: "This value is required", isEmail: "Must be a valid email address", }, }, { element: document.getElementById("issueInfo"), name: "issue", message: { isRequired: "This value is required", isLongerThan: "Must be longer than 10 characters", }, }, ]; const generateMessage = (result, message) => `<p class="${result ? 'text-success' : 'text-danger'}">${message}</p>`; tooltipsInfo.forEach((tooltip) => { const title = Object.entries(tooltip.message) .map(([key, value]) => generateMessage(false, value)) .join(""); tooltip.instance = new twe.Tooltip(tooltip.element, { html: true, template: ` <div> <div class="bg-white text-xs p-4 ms-4 rounded border dark:bg-body-dark dark:border-black/40" data-twe-tooltip-inner-ref></div> </div> `, placement: "right", title, }); }); const tooltipsValidation = new twe.Validation(tooltipsForm); tooltipsForm.addEventListener("valueChanged.twe.validation", (e) => { const element = tooltipsInfo.find( (tooltip) => tooltip.name === e.value.name ); element.instance.dispose(); const title = Object.entries(element.message) .map(([key, value]) => { const result = e.value.validation.find( (res) => key === res.name ).result; return generateMessage(result, value); }) .join(""); const borderColor = e.value.result === "valid" ? "border-success" : "border-danger"; element.instance = new twe.Tooltip(element.element, { html: true, template: ` <div> <div class="bg-white text-xs p-4 ms-4 rounded border dark:bg-body-dark dark:border-black/40" data-twe-tooltip-inner-ref></div> </div> `, placement: "right", title: title, }); });
Show results with Alert
Use the data provided by events to show the validation results with an Alert.
<form id="form-results" data-twe-active-validation="true"> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isLongerThan(2)"> <input type="text" name="name" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleAlertInput1" placeholder="Example label" /> <label for="exampleAlertInput1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Full name </label> </div> <div class="relative mb-3" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isEmail"> <input type="text" name="email" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleAlertInput2" placeholder="Example label" /> <label for="exampleAlertInput2" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[0.9rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Email address </label> </div> <button type="button" class="inline-block rounded bg-primary px-6 pb-2 pt-2.5 text-xs font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-submit-btn-ref> Validate </button> </form> <div id="result-container" class="fixed right-4 top-4 z-[100] mb-3 hidden w-1/4 items-center rounded-lg bg-primary-100 px-6 py-4 text-base text-primary-800 data-[twe-alert-show]:inline-flex" role="alert" data-twe-alert-init data-twe-autohide="true" data-twe-delay="4000"> Result </div>
import { Validation, Input, Alert, initTWE, } from "tw-elements"; initTWE({ Input, Alert }); const resultContainer = document.getElementById("result-container"); const alertInstance = Alert.getInstance(resultContainer); const resultsForm = document.getElementById("form-results"); const resultValidation = new Validation(resultsForm); resultsForm.addEventListener("valid.twe.validation", (e) => { const results = []; resultsForm.querySelectorAll("input").forEach((input) => { results.push({ [input.name]: input.value }); }); resultContainer.innerHTML = ` <div> <h3 class="mb-3">Validation passed! Here is the result:</h3> <div>${JSON.stringify(results, null, 2)}</div> </div> `; alertInstance.show(); });
const resultContainer = document.getElementById("result-container"); const alertInstance = twe.Alert.getInstance(resultContainer); const resultsForm = document.getElementById("form-results"); const resultValidation = new twe.Validation(resultsForm); resultsForm.addEventListener("valid.twe.validation", (e) => { const results = []; resultsForm.querySelectorAll("input").forEach((input) => { results.push({ [input.name]: input.value }); }); resultContainer.innerHTML = ` <div> <h3 class="mb-3">Validation passed! Here is the result:</h3> <div>${JSON.stringify(results, null, 2)}</div> </div> `; alertInstance.show(); });
Custom feedback messages
You can set the feedback messages to have the content you want. Simply add data-twe-invalid-feedback
and data-twe-valid-feedback
attributes with your custom message.

<div> <!-- Left column container with background--> <div class="flex h-full flex-wrap items-center justify-center lg:justify-between"> <div class="shrink-1 mb-12 grow-0 basis-auto md:mb-0 md:w-9/12 md:shrink-0 lg:w-6/12 xl:w-6/12"> <img src="https://tecdn.b-cdn.net/img/Photos/new-templates/bootstrap-login-form/draw2.webp" class="w-full" alt="Sample image" /> </div> <!-- Right column container --> <div class="mb-12 md:mb-0 md:w-8/12 lg:w-5/12 xl:w-5/12"> <form data-twe-validation-init data-twe-active-validation="true"> <!--Sign in section--> <div class="flex flex-row items-center justify-center lg:justify-start"> <p class="mb-0 me-4 text-lg">Sign in with</p> <!-- Facebook --> <button type="button" data-twe-ripple-init data-twe-ripple-color="light" class="mx-1 inline-block h-9 w-9 rounded-full bg-primary fill-white p-2 uppercase leading-normal shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"> <!-- Facebook --> <span class="[&>svg]:mx-auto [&>svg]:h-3.5 [&>svg]:w-3.5"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"> <!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc. --> <path d="M80 299.3V512H196V299.3h86.5l18-97.8H196V166.9c0-51.7 20.3-71.5 72.7-71.5c16.3 0 29.4 .4 37 1.2V7.9C291.4 4 256.4 0 236.2 0C129.3 0 80 50.5 80 159.4v42.1H14v97.8H80z" /> </svg> </span> </button> <!-- X --> <button type="button" data-twe-ripple-init data-twe-ripple-color="light" class=" mx-1 inline-block h-9 w-9 rounded-full bg-primary fill-white p-2 uppercase leading-normal shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"> <!-- X --> <span class="[&>svg]:mx-auto [&>svg]:h-3.5 [&>svg]:w-3.5"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> <!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc. --> <path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z" /> </svg> </span> </button> <!-- Linkedin --> <button type="button" data-twe-ripple-init data-twe-ripple-color="light" class=" mx-1 inline-block h-9 w-9 rounded-full bg-primary fill-white p-2 uppercase leading-normal shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong"> <!-- Linkedin --> <span class="[&>svg]:mx-auto [&>svg]:h-3.5 [&>svg]:w-3.5"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"> <!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc. --> <path d="M100.3 448H7.4V148.9h92.9zM53.8 108.1C24.1 108.1 0 83.5 0 53.8a53.8 53.8 0 0 1 107.6 0c0 29.7-24.1 54.3-53.8 54.3zM447.9 448h-92.7V302.4c0-34.7-.7-79.2-48.3-79.2-48.3 0-55.7 37.7-55.7 76.7V448h-92.8V148.9h89.1v40.8h1.3c12.4-23.5 42.7-48.3 87.9-48.3 94 0 111.3 61.9 111.3 142.3V448z" /> </svg> </span> </button> </div> <!-- Separator between social media sign in and email/password sign in --> <div class="my-4 flex items-center before:mt-0.5 before:flex-1 before:border-t before:border-neutral-300 after:mt-0.5 after:flex-1 after:border-t after:border-neutral-300 dark:before:border-neutral-500 dark:after:border-neutral-500"> <p class="mx-4 mb-0 text-center font-semibold dark:text-neutral-200"> Or </p> </div> <!-- Email input --> <div class="relative mb-6" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isEmail" data-twe-invalid-feedback="The email address is invalid" data-twe-valid-feedback="The email address is valid"> <input type="text" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[2.15] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleFormControlLogin1" placeholder="Email address" /> <label for="exampleFormControlLogin1" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[2.15] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[1.15rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[1.15rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Email address </label> </div> <!-- Password input --> <div class="relative mb-6" data-twe-input-wrapper-init data-twe-validate="input" data-twe-validation-ruleset="isRequired|isLongerThan(6)" data-twe-invalid-feedback="Please provide a password longer than 6 characters" data-twe-valid-feedback="Your password is longer than 6 characters!"> <input type="password" class="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[2.15] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[twe-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-white dark:placeholder:text-neutral-300 dark:autofill:shadow-autofill dark:peer-focus:text-primary [&:not([data-twe-input-placeholder-active])]:placeholder:opacity-0" id="exampleFormControlLogin2" placeholder="Password" /> <label for="exampleFormControlLogin2" class="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[2.15] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[1.15rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[twe-input-state-active]:-translate-y-[1.15rem] peer-data-[twe-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-400 dark:peer-focus:text-primary" >Password </label> </div> <div class="mb-6 flex items-center justify-between"> <!-- Remember me checkbox --> <div class="relative mb-[0.125rem] mt-3 block min-h-[1.5rem] ps-[1.5rem]" data-twe-validate="checkbox" data-twe-validation-ruleset="isChecked"> <input class="relative float-left -ms-[1.5rem] me-[6px] mt-[0.15rem] h-[1.125rem] w-[1.125rem] appearance-none rounded-[0.25rem] border-[0.125rem] border-solid border-secondary-500 outline-none before:pointer-events-none before:absolute before:h-[0.875rem] before:w-[0.875rem] before:scale-0 before:rounded-full before:bg-transparent before:opacity-0 before:shadow-checkbox before:shadow-transparent before:content-[''] checked:border-primary checked:bg-primary checked:before:opacity-[0.16] checked:after:absolute checked:after:-mt-px checked:after:ms-[0.25rem] checked:after:block checked:after:h-[0.8125rem] checked:after:w-[0.375rem] checked:after:rotate-45 checked:after:border-[0.125rem] checked:after:border-l-0 checked:after:border-t-0 checked:after:border-solid checked:after:border-white checked:after:bg-transparent checked:after:content-[''] hover:cursor-pointer hover:before:opacity-[0.04] hover:before:shadow-black/60 focus:shadow-none focus:transition-[border-color_0.2s] focus:before:scale-100 focus:before:opacity-[0.12] focus:before:shadow-black/60 focus:before:transition-[box-shadow_0.2s,transform_0.2s] focus:after:absolute focus:after:z-[1] focus:after:block focus:after:h-[0.875rem] focus:after:w-[0.875rem] focus:after:rounded-[0.125rem] focus:after:content-[''] checked:focus:before:scale-100 checked:focus:before:shadow-checkbox checked:focus:before:transition-[box-shadow_0.2s,transform_0.2s] checked:focus:after:-mt-px checked:focus:after:ms-[0.25rem] checked:focus:after:h-[0.8125rem] checked:focus:after:w-[0.375rem] checked:focus:after:rotate-45 checked:focus:after:rounded-none checked:focus:after:border-[0.125rem] checked:focus:after:border-l-0 checked:focus:after:border-t-0 checked:focus:after:border-solid checked:focus:after:border-white checked:focus:after:bg-transparent rtl:float-right dark:border-neutral-400 dark:checked:border-primary dark:checked:bg-primary" type="checkbox" value="" id="exampleFormControlLogin3" /> <label class="inline-block ps-[0.15rem] hover:cursor-pointer" for="exampleFormControlLogin3"> Remember me </label> </div> <!--Forgot password link--> <a class="mt-3" href="#!">Forgot password?</a> </div> <!-- Login button --> <div class="text-center lg:text-left"> <button type="button" class="inline-block w-full rounded bg-primary px-7 pb-2 pt-3 text-sm font-medium uppercase leading-normal text-white shadow-primary-3 transition duration-150 ease-in-out hover:bg-primary-accent-300 hover:shadow-primary-2 focus:bg-primary-accent-300 focus:shadow-primary-2 focus:outline-none focus:ring-0 active:bg-primary-600 active:shadow-primary-2 dark:shadow-black/30 dark:hover:shadow-dark-strong dark:focus:shadow-dark-strong dark:active:shadow-dark-strong" data-twe-ripple-init data-twe-ripple-color="light" data-twe-submit-btn-ref> Login </button> <!-- Register link --> <p class="mb-0 mt-2 pt-1 text-sm font-semibold"> Don't have an account? <a href="#!" class="text-danger transition duration-150 ease-in-out hover:text-danger-600 focus:text-danger-600 active:text-danger-700" >Register</a > </p> </div> </form> </div> </div> </div>
// Initialization for ES Users import { Validation, Input, Ripple, initTWE, } from "tw-elements"; initTWE({ Validation, Input, Ripple });