Optimising an email input

We can do better than standard pattern validation.

Gav McKenzie
Author
Gav McKenzie
Published
Apr 5, 2024
Topics
Industry, User experience

I’ve been optimising email inputs in a couple of projects this week and we added a few bits of “hidden until it’s useful” functionality that might be helpful if you are trying to write a better email input.

The base HTML

Other than content, good HTML is the most important part of web software. The back-end code, CSS and JavaScript all exist to get the right HTML into the browser so let’s start there.

<label for="field-id">
Email
</label>
<input
id="field-id"
name="field-name"
type="email"
placeholder="e.g. [email protected]"
value=""
required
/>

This is the bare bones of a decent email input.

The input has a label, associated by field-id, which can be read by users and will be announced by screen readers as the label for the input.

We’ve set the type to email, which will pull up the right keyboard in mobile browsers (includes an @ key on the same view as the letters amongst other tweaks for user convenience) and trigger the built-in email pattern validation in HTML for us.

I’ve added a placeholder as a formatting/value hint for the expected content. Placeholders disappear as soon as a user types in the input, so you can’t rely on them as actual input labels, but they are nice as a formatting hint for the data shape expected.

We’ve also popped on the required attribute to signal to the browser that this field is required. We’ll enhance this field with some more extensive JavaScript validation further down.

Autocomplete

Depending on the context, you can add an autocomplete attribute to the email field to help the browser automatically offer suggestions to fill in the field. The most likely choices for this field are email (the user's primary contact email) and username when used in a login form.

<input
[...other attributes]
autocomplete="email"
/>

This will trigger a suggestion box when the field is focused to help the user fill out the form without needing to type.

autocomplete.webp

Validation

HTML validation is pretty great, but it can be limited. We wanted to extend the email field with a little more extensive validation so we’re using the vee validate library, as this is on a Vue project.

The first two standard validators to add to the field are required (to check it’s populated) and email to check it matches an email format. This gets us back up to speed with the HTML validation we added earlier. These two come as built-in vee validate rules, so I’ll let you check them out over there.

Some users occasionally try to use disposable email addresses, which can make them a bit awkward to contact later. We’ve used the disposable email domains wildcards package to check that the email doesn’t look like a throwaway.

defineRule('disposable', (value: string) => {
// Because the wildcards only contain tlds,
// we need to check if the wildcard exists in the
// email address, rather than checking the email domain
// exists in the wildcards
const domain = value.split('@')[1]
const wildcard = wildcards.find((wildcard) => domain.includes(wildcard))
return wildcard
? 'This looks like a disposable email address. Please provide a valid email address.'
: true
})

We also use an async validator in some instances, to check that the email is unique in the system. As our users use their email to log in, we don’t want duplicate emails.

async unique(value: string) {
try {
const isUnique = await isUserEmailUnique(value)
return isUnique || 'This email is already in use'
} catch (error: any) {
[... deal with unexpected errors]
}
},

You have to be careful with the context in which a unique check is handled as it could expose active emails in your system, allowing phishing attacks. If someone knows who your users are, they can send fake emails to them pretending to be from your service to try and capture information.

Suggestions

Our final input enhancement uses the mailcheck library to check for common domain misspellings and suggests a correction. This catches those keyslips that turn gmail into gnail.

checkSpelling(value?: string) {
mailcheck.run({
email: value || '',
suggested: (suggestion: { full: string }) => {
this.suggestion = suggestion.full
},
empty: () => {
this.suggestion = ''
},
})
},

suggest.webp

When the user clicks the suggestion link (which is actually a button element, thanks HTML!), the input value is corrected and re-validated.

The final result

Our final input helps users get to the correct keys on the keyboard, tries to fill out the field for them to prevent mistakes, validates it looks like a real email address and not a disposable one and, if a mistake has been made, offers helpful suggestions to get them back on the right track. It won’t catch everything, but it’s a damn good start at providing more than just bog standard email validation.