Create Amazing Forms

Provide real-time validation

Real-time data validation doesn't just help to keep your data clean, but it also helps improve the user experience. Modern browsers have several built-in tools to help provide real-time data validation and may prevent the user from submitting an invalid form. Visual cues should be used to indicate whether a form has been completed properly.

TL;DR

  • Leverage the browser's built-in validation attributes like pattern, required, min, max, etc.
  • Use JavaScript and the Constraints Validation API for more complex validation requirements.
  • Show validation errors in real time, and if the user tries to submit an invalid form, show all fields they need to fix.

Use these attributes to validate input

The pattern attribute

The pattern attribute specifies a regular expression used to validate an input field. For example, to validate a US Zip code (5 digits, sometimes followed by a dash and an additional 4 digits), we would set the pattern like this:

<input type="text" pattern="^\d{5,6}(?:[-\s]\d{4})?$" ...>
Common regular expression patterns
Description Regular expression
Postal address [a-zA-Z\d\s\-\,\#\.\+]+
Zip Code (US) ^\d{5,6}(?:[-\s]\d{4})?$
IP Address (IPv4) ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
IP Address (IPv6) ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$
IP Address (both) ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]).){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$
Credit Card Number ^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})$
Social Security Number ^\d{3}-\d{2}-\d{4}$
North American Phone Number ^(?:(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$

The required attribute

If the required attribute is present, then the field must contain a value before the form can be submitted. For example, to make the zip code required, we’d simply add the required attribute:

<input type="text" required pattern="^\d{5,6}(?:[-\s]\d{4})?$" ...>

The min, max and step attributes

For numeric input types like number or range as well as date/time inputs, you can specify the minimum and maximum values, as well as how much they should each increment/decrement when adjusted by the slider or spinners. For example, a shoe size input would set a minumum size of 1 and a maximum size 13, with a step of 0.5

<input type="number" min="1" max="13" step="0.5" ...>

The maxlength attribute

The maxlength attribute can be used to specify the maximum length of an input or textbox and is useful when you want to limit the length of information that the user can provide. For example, if you want to limit a filename to 12 characters, you can use the following.

<input type="text" id="83filename" maxlength="12" ...>

The novalidate attribute

In some cases, you may want to allow the user to submit the form even if it contains invalid input. To do this, add the novalidate attribute to the form element, or individual input fields. In this case, all pseudo classes and JavaScript APIs will still allow you to check if the form validates.

<form role="form" novalidate>
  <label for="inpEmail">Email address</label>
  <input type="email" ...>
</form>

Remember

  • Even with client-side input validation, it is always important to validate data on the server to ensure consistency and security in your data.

Use JavaScript for more complex real-time validation

When the built-in validation plus regular expressions aren’t enough, you can use the Constraint Validation API, a powerful tool for handling custom validation. The API allows you to do things like set a custom error, check whether an element is valid, and determine the reason that an element is invalid:

API Description
setCustomValidity() Sets a custom validation message and the customError property of the ValidityState object to true.
validationMessage Returns a string with the reason the input failed the validation test.
checkValidity() Returns true if the element satisfies all of it's constraints, and false otherwise.
validity Returns a ValidityState object representing the validity states of the element.

Set custom validation messages

If a field fails validation, use setCustomValidity() to mark the field invalid and explain why the field didn’t validate. For example, a sign up form might ask the user to confirm their email address by entering it twice. Use the blur event on the second input to validate the two inputs and set the appropriate response. For example:

    if (input.value != primaryEmail) {
      // the provided value doesn't match the primary email address
      input.setCustomValidity('The two email addresses must match.');
      console.log("E-mail addresses do not match", primaryEmail, input.value);
    } else {
      // input is valid -- reset the error message
      input.setCustomValidity('');
    }
    
View full sample

Prevent form submission on invalid forms

Because not all browsers will prevent the user from submitting the form if there is invalid data, you should catch the submit event, and use the checkValidity() on the form element to determine if the form is valid. For example:

    form.addEventListener("submit", function(evt) {
      if (form.checkValidity() === false) {
        evt.preventDefault();
        alert("Form is invalid - submission prevented!");
        return false;
      } else {
        // To prevent data from being sent, we've prevented submission
        // here, but normally this code block would not exist.
        evt.preventDefault();
        alert("Form is valid - submission prevented to protect privacy.");
        return false;
      }
    });
    
View full sample

Show feedback in real-time

It’s helpful to provide a visual indication on each field that indicates whether the user has completed the form properly before they’ve submitted the form. HTML5 also introduces a number of new pseudo-classes that can be used to style inputs based on their value or attributes.

Pseudo-class Use
:valid Explicitly sets the style for an input to be used when the value meets all of the validation requirements.
:invalid Explicitly sets the style for an input to be used when the value does not meet all of the validation requirements.
:required Explicitly sets the style for an input element that has the required attribute set.
:optional Explicitly sets the style for an input element that does not have the required attribute set.
:in-range Explicitly sets the style for a number input element where the value is in range.
:out-of-range Explicitly sets the style for a number input element where the value is out of range.

Validation happens immediately which means that when the page is loaded, fields may be marked as invalid, even though the user hasn’t had a chance to fill them in yet. It also means that as the user types, and it’s possible they’ll see the invalid style while typing. To prevent this, you can combine the CSS with JavaScript to only show invalid styling when the user has visited the field.

    input.dirty:not(:focus):invalid {
      background-color: #FFD9D9;
    }
    input.dirty:not(:focus):valid {
      background-color: #D9FFD9;
    }
    
View full sample
    var inputs = document.getElementsByTagName("input");
    var inputs_len = inputs.length;
    var addDirtyClass = function(evt) {
      sampleCompleted("Forms-order-dirty");
      evt.srcElement.classList.toggle("dirty", true);
    };
    for (var i = 0; i < inputs_len; i++) {
      var input = inputs[i];
      input.addEventListener("blur", addDirtyClass);
      input.addEventListener("invalid", addDirtyClass);
      input.addEventListener("valid", addDirtyClass);
    }
    
View full sample

Important

  • You should show the user all of the issues on the form at once, rather than showing them one at a time.

Updated on 2014-04-30

Authors

Pete LePage

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the Apache 2.0 License. For details, see our Site Policies.