Form validation
Quiet form controls are form-associated custom elements, meaning their values will submit just like native form controls when you give them a name.
To align with the platform, validation is built on top of the Constraint Validation API. This allows Quiet's form controls to work seamlessly with native form validation techniques. You can use most standard HTML validation attributes and access validation states and methods in the same way as native form controls.
Basic validation Jump to heading
The simplest way to validate a form control is with HTML validation attributes. For example, you can make a
text field required by adding the required
attribute.
<form action="about:blank" target="_blank"> <quiet-text-field name="username" label="Username" required style="margin-block-end: 1rem;" ></quiet-text-field> <quiet-button type="submit">Submit</quiet-button> </form>
Each component defines its own validation attributes, but the most common ones include:
required
- Makes the field requiredpattern
- Validates against a regular expressionminlength
+maxlength
- Sets minimum and maximum length requirementsmin
+max
- Sets minimum and maximum value requirementsstep
- Controls value incrementstype
- Validates against a specific input type (e.g. email, url, number)
Refer to each component's documentation for a complete list of supported attributes.
Custom validation messages Jump to heading
While browsers provide default validation messages, you can customize them using the
setCustomValidity()
method, just like native form controls.
<form action="about:blank" target="_blank"> <quiet-text-field id="password" name="password" label="Password" required minlength="8" style="margin-block-end: 1rem;" ></quiet-text-field> <quiet-button type="submit">Submit</quiet-button> </form> <script type="module"> const password = document.querySelector('#password'); await customElements.whenDefined('quiet-text-field'); password.setCustomValidity('The cats will not be happy until your password is at least 8 characters'); </script>
Remember to clear the custom validity message by calling setCustomValidity('')
when the input
becomes valid, otherwise the field will remain invalid and won't allow the form to submit.
Validation states Jump to heading
Form controls have various states that reflect their validation status. You can use these states for styling or to provide feedback to users.
<quiet-text-field id="email" name="email" label="Email" type="email" required ></quiet-text-field> <script> const email = document.querySelector('#email'); // Check if the field is valid console.log(email.validity.valid); // Check specific validation states console.log(email.validity.valueMissing); // is required but empty console.log(email.validity.typeMismatch); // doesn't match type="email" </script>
Validation states are updated automatically as the user interacts with the form control. You can also
trigger validation programmatically using the
reportValidity()
method. To check validity without showing the browser's error message,
checkValidity()
instead.
Styling validation Jump to heading
Quiet form controls expose custom states you can target with CSS. These states make it easy to style controls based on their current state.
<form action="about:blank" target="_blank" id="example__styling"> <quiet-text-field name="email" label="Email" type="email" placeholder="Enter a value and blur the field" required id="example__styling" ></quiet-text-field> <quiet-button type="submit">Submit</quiet-button> <quiet-button type="reset">Reset</quiet-button> </form> <style> #example__styling { quiet-text-field:state(user-valid) { outline: solid 2px var(--quiet-constructive-stroke-mid); outline-offset: .5rem; } quiet-text-field:state(user-invalid) { outline: solid 2px var(--quiet-destructive-stroke-mid); outline-offset: .5rem; } quiet-text-field { margin-block-end: 1rem; } } </style>
The following pseudo-classes are available to style all form controls.
:valid
- Applied immediately and always when the control is valid.:invalid
- Applied immediately and always when the control is invalid.
You can also use various custom states, which must be wrapped in the
:state()
selector. Here are some examples, but remember that not all components implement all of these states, so
please refer to the respective component's documentation.
:state(disabled)
- Applied when the control is disabled:state(blank)
- Applied when the control has a blank value:state(focused)
- Applied when the control has focus-
:state(user-valid)
- Applied when the control is valid and the user has interacted with it -
:state(user-invalid)
- Applied when the control is invalid and the user has interacted with it
The user-valid
and user-invalid
states are only applied after user interaction
to prevent premature validation feedback.
Validating entire forms Jump to heading
To validate an entire form, you can use the form's checkValidity()
method. This will trigger
validation on all form controls within the form, including Quiet and native elements.
<form action="about:blank" target="_blank" id="signup-form"> <quiet-text-field name="username" label="Username" required style="margin-block-end: 1rem;" ></quiet-text-field> <quiet-text-field name="email" label="Email" type="email" required style="margin-block-end: 1rem;" ></quiet-text-field> <quiet-button type="submit"> Sign up </quiet-button> </form> <script> const form = document.querySelector('#signup-form'); form.addEventListener('submit', event => { if (!form.checkValidity()) { event.preventDefault(); } }); </script>