5 Tips for Building Delightful, Robust Forms

I hate bad forms.
TipsWeb
The stack
Next.js
Tailwind
Vercel
Zod
RHF
TypeScript

I have a vendetta against bad forms. I’ve seen too many forms that are a pain to fill out, and I’m on a mission to eradicate them.

I’m diving into a project with a guinea pig breeding club, revamping their public registration forms for guinea pigs in their studbook, and I wanted to share my thoughts.

When it comes to forms, some practices are universally beneficial:

  • Immediate focus on invalid fields
  • Dropdown menus for multiple options.
  • Clearly indicating required fields.

Yet, what transforms a form from good enough to robust and delightful?

Resumability

A user can exit the form prematurely for a myriad of reasons: browser crashed, closed the page accidentially, dog spilled coffee on their laptop, etc.

When they come back, the form should remember where they were and restore their progress — depending on how long they’ve been gone.

I used this rule of thumb:

  • If they’re gone for less than 30 minutes, restore their progress immediately
  • If they’re gone for more than 30 minutes, prompt them if they want to continue or start again
  • If they’re gone for more than 5 days, discard their progress and start again

For this project, I save the state to the browser’s localStorage along with a date and check it when the user returns.

The user can restore their process or discard and try again

Flexible Navigation

Forms are linear, but human thinking isn’t.

Users might realize they need to tweak something back on step 2 while already on step 6.

Allow users to backtrack and make adjustments without losing progress.

Bonus points: On the summary page, add links that take them straight back to the relevant step.

The user can go straight back to the relevant step

Lookup

If the input needs to connect to an existing database entry, the user should be able to look it up — not just enter a name or ID number with no feedback.

I did this by asking the user to enter a pedigree number (a form of ID number for guinea pigs) and showing them the results to confirm.

From a security perspective: Make sure you limit information from the lookup, and maybe put the API behind a rate limiter. You don’t want someone to be able to brute force the entire database.

This also opens you up to further validation early on, such as “this guinea pig is the wrong sex to be the mother”.

The user is shown the matching database record

Smart Defaults

You can make educated guesses on many fields, inferring from other fields or a logged-in user’s profile.

It’s important they can still change the guesses if it’s wrong: For many people their initials the first letter of their names, but not everyone.

Never ask the user for the same information twice. When you can, infer from what they’ve already provided. For example:

  • If you know their date of birth, you know their age.
  • If you know their name, you know their initials.
  • If you know their country, you know the country code for their phone number.

Proactive

Warn the user early of possible mistakes.

In this project, the user must add details for the parents of the guinea pig they are registering.

If they enter a date of birth for the parents that make it unlikely they could have had the child, I warn them — but don’t prevent them from continuing, as there are exceptions.

This is particularly important as we’re dealing with incomplete or incorrect legacy data in the database as a failed form validation could be bad data, not user input.

The user is told that the guinea pig is the wrong sex.

That’s it. Cheers! 🍻