Creating tools for Developers

DATE 2026-02-14 -- TIME 20:07:57

Description

Bo Knows Forms // Thoughts

In this post, I will share my objectively correct opinions on the guaranteed only best way to do form validation

The purpose of form validation is fairly straightforward. It’s the set of mechanisms by which a web form checks if the information provided by a user is correct and valid. The process on how to do form validation is less cut and dry.

1. Prompts

Form validation should first aim to be as accommodating as possible for different forms of input based on particular data types. In doing so, sensible labels and text inputs should have a placeholder that shows an example.

Labels should be tagged with the \

<label for="signup_email">Email</label> <input type="email" id="signup_email" placeholder="joe@example.com" />

Using the placeholder to hold the title isn’t the worst thing you can do. While it saves space, which can be helpful on mobile, it’s not preferred. One advantage of always having a label is you have a place to show a related error message.


<label for="signup_email">Email</label>
<span>Note: Email is required (e.g. joe@example.com)</span>
<input type="email" id="signup_email" placeholder="joe@example.com" />

2. Inputs

There are a few major considerations when it comes to inputs. These include

  • Input types (and validation)
  • Localization/internationalization (l10n/i18n)
  • Usability

Input Types

First, check your input types. E-mail should have <input type="email">, numbers should have <input type="number">.

That said, there are some input types that you might avoid. For instance, “date” was, for a long time, notoriously poor on Apple Safari. These are always improving, though. For the love of god, use the email, password, input, number, and url types. date has come a long way, too.

For credit-cards and addresses, you should be thinking about the autocomplete attribute. cc-name and cc-number are biggies, also street-address. Once again I’ll defer to Mozilla for the exhaustive list.

When it comes to validation, less is often more. Email address validation is famously complex - the best you can do is to verify there’s exactly one @, followed by something that looks like a domain. I can’t tell you how many times my thegray.company email address gets rejected by email validators that are using a fixed - and outdated! - list of top-level domains. Or password validators that don’t support long pass phrases.

Localization/Internationalization

Here’s where localization (l10n) comes in. This is distinct from internationalization (18n). i18n says that “month” in Japanese is “月”, but localization says that Japanese order their dates “year, month, day”. This is next-level good behavior.

No matter the order, feel free to use a drop-down for month and year, but I insist on an open input for day. No one will confuse which is which, guaranteed, and selecting from a drop-down with more than ~25 items is tedious. Just validate that it’s a number.

Usability

My next hill to die on is addresses. It is intuitive to make address inputs in the same order that we put them on envelopes.

Intuitive, but… not helpful.

Put the country first, then the postal code, and use that information to autofill the other inputs.

And did you notice I said, “put country first”!? If you are designing a credit card entry, and validate that “postal code” is “a United States formatted zip code”, you need to get out more.

News flash: not all countries are the United States. Honestly, if you’re validating credit card inputs on the client, you’re doing it wrong. Let the credit card company deal with it (send it to them for validation). This carries over to all third-party services – do not hard-code their validation rules in your app.

You should be parsing their error messages and forwarding them to the user. If the service has terrible messages - or contains no error messages - is it a bank? Banks are the worst. In that case I would put validators after the attempt to submit the data as-is.

What I mean is: assume success, and on error then run your own validators. So if the service changes what is “valid” your error messages will be wrong but your user won’t be blocked.

3. Error Messages

The most important word here is Actionable. Breaking our task down further, our error messages should:

  • Timing: appears when the user has shown their intent to submit the form (not sooner)
  • Visibility: appears everywhere the user might look
  • Actionable: describe what action to take to resolve the error

Timing

The first bullet is easy: when the user first arrives, let them move freely around the form. Maybe they’re gaining context, maybe they copied some text into their clipboard and want to paste that into your “comment” form before entering their email. Who knows!? And you shouldn’t care. Just wait until they press submit before showing the errors.

Now that the errors are visible - when do you hide them?

I’ll give you a choice: when they press submit again, or continuously while they are editing. e.g. The “Email is required” message should go away when they enter an email – but then do you also show “Valid email is required” during this editing? Ideally not, so… waiting until they press submit is simple, and simple is usually best.

Visibility

Next, visibility. You have more options here, depending on the complexity of your form. If you have a survey with 30 questions, you don’t want to summarize 30 validation errors at the top of the page.

I’m generally opposed to “scroll to X” behavior, like “scroll to top” when submitting a form. The reason to do it is often due to another UX issue. If your errors are only printed at the top of the form, which is at the top of the page, then it “makes sense” to scroll to the top. You’re fixing the wrong problem! Show the errors where the user is. In the case of the submit button - you can get away with a generic “there are errors” message, as long as the error messages are visible in the form.

I hate you if you show “there are errors” and nothing else. Banks, again, this is you I’m talking to.

So yeah I’m not a huge fan of “automatically scroll to errors”, but it beats the pants off of a page that doesn’t do a good job informing the user (1) that there was a problem and (2) where the problem is.

Actionable

You should have a generic “there was a problem” message at the top and bottom, then highlight the sections that were incorrect, along with an error message. If your form is doing client-side validation, make sure to show a “there was a problem” message near the submit button! Otherwise, your user will have no way to know why the button isn’t working.

If you have a short form, like email and password entry, your users would be well served by grouping the error messages at the top, because they can view everything at a glance.

I prefer, instead, clickable error messages that take you to the appropriate field.

Published: 2023-01-13