Developers

HTML5 form validation without writing JavaScript

A practical guide to HTML5 form validation using required, pattern, input types and length limits, with copy-ready examples and styling tips.

Cover image: HTML5 form validation without writing JavaScript, a FormDoc guide

HTML5 form validation lets the browser check a form before it’s sent, using plain attributes like required, type="email", pattern and maxlength. If a field fails, the browser stops the submission and shows a message next to the field. You don’t need any JavaScript, and it works in every modern browser.

It isn’t a security feature (anyone can bypass it), but it’s the quickest way to catch honest mistakes like an empty message box or a typo in an email address.

Built-in validation runs instantly, in the visitor’s own language, with messages screen readers already know how to announce. It costs you a few attributes. Compared with writing your own checks in JavaScript, that’s a very good deal.

It also fits nicely with a form backend. When the browser has already checked the basics, fewer broken submissions reach your inbox, and visitors fix problems on the spot instead of waiting for an error page.

The required attribute

The simplest rule: the field can’t be empty.

<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>

It works on text inputs, textareas, selects, checkboxes and radio buttons. For a group of radio buttons, putting required on one of them makes the whole group required.

Use it only on fields you truly need. A form where everything is required feels like an interrogation, and visitors may give up rather than fill it all in.

Input types do half the work

Choosing the right type gives you validation and a better keyboard on phones, all at once:

Input type What the browser checks Phone keyboard
email Looks like an email address (has an @ and a domain part) Email layout with @
url Looks like a full URL with a scheme URL layout
tel Nothing by default (phone formats vary too much) Number pad
number A number, within min and max, matching step Numeric
date A valid date, within min and max Date picker
text Nothing unless you add pattern or length limits Standard

Notice that tel doesn’t validate anything. That’s deliberate: phone numbers come in many formats, and rejecting a perfectly good number is worse than accepting an odd one. If you want a rule, add a gentle pattern, but think twice first.

Length limits: minlength and maxlength

These limit how many characters a field accepts:

<label for="name">Your name</label>
<input id="name" name="name" type="text" minlength="2" maxlength="100" required>

maxlength stops typing at the limit, so visitors can’t overshoot. minlength shows a message if the value is too short, but only once the visitor has typed something; an empty field still needs required.

A sensible maxlength on your message box is a good habit. FormDoc accepts up to 10,000 characters per field and 64 KB of text per submission, so maxlength="10000" on a textarea means visitors find out about the limit while typing, not after pressing Send.

Custom rules with pattern

pattern takes a regular expression that the whole value must match. Pair it with a title that explains the rule in plain words, because browsers often show it in the error message:

<label for="pincode">PIN code</label>
<input id="pincode" name="pincode" type="text"
       inputmode="numeric" pattern="[0-9]{6}"
       title="Six digits, for example 560001" required>

A few tips for patterns:

  • The pattern must match the entire value. You don’t need ^ and $; the browser adds them for you.
  • Keep it forgiving. Allow spaces where people naturally type them, or strip them on the server.
  • Explain the format in the label or hint text too. Don’t make visitors discover the rule by failing it.
  • Use inputmode to bring up the right keyboard without changing the input type.

Styling valid and invalid fields

CSS can react to validation state. The older :invalid selector matches as soon as the page loads, which means empty required fields look like errors before anyone has touched them. The newer :user-invalid selector only matches after the visitor has interacted with the field, which feels much kinder:

input:user-invalid,
textarea:user-invalid {
  border-color: #b42318;
}

Never rely on colour alone to show an error. Keep the browser’s message, or add a short text hint, so people who can’t see the colour still know what went wrong.

When you need a little JavaScript

Built-in validation covers most forms. A few cases need more:

  1. Comparing two fields, such as “confirm email”. Use setCustomValidity() to set a message on the second field when they don’t match, and clear it with an empty string when they do.
  2. Custom error display. Add novalidate to the form to switch off the browser’s bubbles, then use the Constraint Validation API (checkValidity(), validity) to show your own messages.
  3. Sending without a page change. If you submit with fetch, call form.reportValidity() first so the browser still checks everything.

If your script listens for the form’s submit event, the browser has already run its checks, because that event only fires once the form is valid. Your required and pattern rules keep working. The post on submitting a form without page reload covers that setup.

HTML5 form validation is not enough on its own

Browser checks are a convenience for honest visitors. Bots and scripts post straight to your endpoint and skip them entirely. So the server must enforce its own limits.

FormDoc does this for you on every submission: it rejects payloads over its size limits, checks captcha tokens if you’ve turned a captcha on, and runs spam scoring before anything reaches your inbox. If you send submissions with JavaScript and Accept: application/json, errors come back as a code and a message you can show; the API reference lists them.

Common questions

Can I change the browser’s default validation messages?

Yes, with setCustomValidity() in JavaScript. Set the message when the field is invalid and reset it to an empty string when it’s valid, or the field will stay invalid. For most forms, the default messages are clear and already translated, so only customise when you need to.

Does type="email" check that the address really exists?

No. It only checks that the text looks like an email address. Whether the mailbox exists can’t be known until you send a message to it.

Is HTML5 form validation accessible?

Mostly, yes. Browsers announce built-in messages to screen readers and move focus to the first invalid field. Make sure every input has a proper <label>, and add visible hint text for any format rules.

Put it into practice

If you’re still deciding which name, action and method to use, read HTML form action and method explained. Then take one of the form templates, add the attributes above, and connect it with a free key from app.formdoc.dev/new.

Keep reading