Ronalds Vilciņš

What HTML forms can do without JavaScript

Forms collect JavaScript remarkably quickly. Email validation gets a library, numeric limits get a custom handler, and a mobile keyboard problem gets another workaround.

A developer needs email validation, so a library appears. A field needs a minimum value, so a custom script is added. Mobile users need a better keyboard, so another workaround is built.

Modern HTML already handles many of these cases.

That does not mean you never need JavaScript in a form. Complex forms still need custom logic. But it is worth learning what the browser can do before rebuilding it yourself.

Start with the value the field accepts

This works:

<input type="text">

But it tells the browser almost nothing.

For email:

<input type="email">

For URLs:

<input type="url">

For phone numbers:

<input type="tel">

For search:

<input type="search">

For numbers:

<input type="number">

The correct type can improve validation, mobile keyboards, and browser behaviour.

Required fields

<input
  type="email"
  name="email"
  required
>

The browser can stop the form from being submitted when the field is empty.

Minimum and maximum values

For numbers:

<input
  type="number"
  min="1"
  max="10"
>

For dates:

<input
  type="date"
  min="2026-01-01"
  max="2026-12-31"
>

For text length:

<input
  type="text"
  minlength="3"
  maxlength="50"
>

These basic rules do not need a validation library.

Step values

<input
  type="number"
  min="0"
  step="0.5"
>

Or:

<input
  type="number"
  min="0"
  step="0.01"
>

Pattern matching

For some text fields, you can use pattern:

<input
  type="text"
  pattern="[A-Z]{2}[0-9]{4}"
>

That example expects two capital letters followed by four digits.

Do not turn pattern into a huge unreadable regular expression if a simpler form design would work better.

Useful autocomplete

The autocomplete attribute helps browsers fill forms correctly.

<input
  name="name"
  autocomplete="name"
>
<input
  type="email"
  name="email"
  autocomplete="email"
>
<input
  name="street"
  autocomplete="street-address"
>
<input
  name="postal"
  autocomplete="postal-code"
>

This can make checkout and contact forms much faster to complete.

One-time codes

<input
  type="text"
  inputmode="numeric"
  autocomplete="one-time-code"
>

On supported phones, the operating system may offer a received verification code automatically.

Better mobile keyboards

Use inputmode when the expected keyboard is different from the data type.

<input
  type="text"
  inputmode="numeric"
>

This is useful for codes made only from digits.

Remember that inputmode does not validate the value. It only helps the browser choose a keyboard.

Labels matter

Always connect a visible label to its field.

<label for="email">
  Email address
</label>

<input
  id="email"
  name="email"
  type="email"
>

Or wrap the field:

<label>
  Email address
  <input name="email" type="email">
</label>

Do not use placeholder text as the only label.

Help text

If a field needs more explanation:

<label for="password">
  Password
</label>

<input
  id="password"
  type="password"
  aria-describedby="password-help"
>

<p id="password-help">
  Use at least 12 characters.
</p>

Now assistive technology can connect the help text to the field.

Native validation states

CSS can style validation states.

input:user-invalid {
  border-color: red;
}
input:user-valid {
  border-color: green;
}

I prefer :user-invalid over :invalid in many forms because it avoids showing an error before the user has interacted with the field.

Do not rely only on color

A red border alone is not a complete error message.

Users should also get clear text explaining what went wrong.

For more complex validation, JavaScript can still be useful for better messages.

Use <fieldset> and <legend> for related radio buttons or checkboxes.

<fieldset>
  <legend>Preferred contact method</legend>

  <label>
    <input type="radio" name="contact" value="email">
    Email
  </label>

  <label>
    <input type="radio" name="contact" value="phone">
    Phone
  </label>
</fieldset>

This gives the group a clear name.

A simple contact form

<form action="/contact" method="post">
  <label for="name">
    Name
  </label>

  <input
    id="name"
    name="name"
    autocomplete="name"
    required
  >

  <label for="email">
    Email
  </label>

  <input
    id="email"
    name="email"
    type="email"
    autocomplete="email"
    required
  >

  <label for="message">
    Message
  </label>

  <textarea
    id="message"
    name="message"
    minlength="10"
    required
  ></textarea>

  <button type="submit">
    Send message
  </button>
</form>

This already has useful labels, autocomplete, email validation, required fields, and a minimum message length.

No JavaScript is needed for those basics.

Server-side validation still matters

Browser validation helps the user, but never trust it as your only validation.

A user can bypass client-side checks.

Your server should still validate required values, formats, limits, permissions, and anything that affects security or stored data.

Start with HTML

My preferred order is:

HTML input type
↓
HTML validation attributes
↓
autocomplete / inputmode
↓
clear labels and help text
↓
CSS states
↓
JavaScript only for rules HTML cannot handle

That usually leads to a smaller and more reliable form.

HTML forms are not perfect, and server-side validation is still required. Even so, I would exhaust the native attributes before recreating their behaviour in JavaScript.

Enjoyed reading this?

If you found this article helpful, consider supporting my writing. Every little bit helps keep this blog going.

← Back to all articles