Encodera

Thanks for reading!

Donate to the site — we work hard for you.

How to validate a form before submitting

Short answer

Attributes like required, minlength and type="email" already check the fields. The browser does not submit the form until they are filled correctly:

<form id="form">
  <input name="name" required minlength="2" placeholder="Name">
  <input name="email" type="email" required placeholder="Email">
  <input name="age" type="number" min="18" max="120" placeholder="Age">
  <button>Send</button>
</form>
<p id="out"></p>
<script>
  document.getElementById('form').addEventListener('submit', (event) => {
    event.preventDefault();
    document.getElementById('out').textContent = 'All fields are valid';
  });
</script>

Custom rules

setCustomValidity() adds your own check. A non-empty message makes the field invalid, an empty string makes it valid again:

const password = form.elements.password;
const repeat = form.elements.repeat;
 
repeat.addEventListener('input', () => {
  repeat.setCustomValidity(repeat.value === password.value ? '' : 'Passwords do not match');
});

Your own error output

The novalidate attribute on the form turns off the browser messages, and you show the errors yourself, for example in elements with the class error and a data-for attribute with the field name:

form.addEventListener('submit', (event) => {
  if (!form.checkValidity()) {
    event.preventDefault();
    for (const field of form.querySelectorAll('input')) {
      const error = form.querySelector(`.error[data-for="${field.name}"]`);
      if (error) error.textContent = field.validationMessage;
    }
  }
});

The same rules without a browser

Checks written as plain functions can be reused anywhere, including on the server:

const validate = (data) => {
  const errors = {};
  if (data.name.trim().length < 2) errors.name = 'Name is too short';
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) errors.email = 'Invalid email';
  return errors;
};
 
console.log(validate({ name: 'A', email: 'anna@example.com' }));
console.log(Object.keys(validate({ name: 'Anna', email: 'anna@example.com' })).length);

Output:

{
  "name": "Name is too short"
}
0

Common mistake

Checks in the browser are only for convenience. Anyone can send data to the server bypassing the form, so the server must check everything again.

// a request without any form at all
await fetch('/api/signup', { method: 'POST', body: JSON.stringify({ email: 'not-an-email' }) });

Functions used in this recipe

Thanks for reading!

Donate to the site — we work hard for you.