All articles
Design & UXJuly 24, 2026 6 min read

Form Field Errors: Why Yours Are Failing Users and How to Fix Them

Inline validation looks helpful on the Figma frame and hostile in production. Here's how to design error states that guide users through a form instead of punishing them for typing.

Form Field Errors: Why Yours Are Failing Users and How to Fix Them

Most forms we audit get validation wrong in the same three places: they yell too early, they whisper too late, and they leave screen reader users guessing. The Figma file rarely captures any of this because errors live in states, timing, and focus behaviour — none of which a static frame can show.

This is the pattern set we reach for when we rebuild a checkout, a signup, or a KYC flow and need the error UX to stop leaking conversions.

The three failure modes we see on every audit

Before fixing anything, name what's broken. In almost every form review we do, the same issues show up:

  1. Premature validation. The field turns red the moment a user types the first character of their email. They haven't finished. You're wrong. They feel scolded.
  2. Silent failures on submit. The form scrolls back to the top, shows a generic "Please check your input" banner, and the user has to hunt for the offending field.
  3. Invisible errors for assistive tech. The red border and the little icon mean nothing to a screen reader. aria-invalid is missing, the error text isn't associated with the input, and focus never moves.

Each of these has a specific fix, and the fixes compound. Get all three right and abandonment on long forms drops noticeably — in our experience somewhere in the range of a few percentage points on checkout, more on multi-step onboarding.

Timing: when to validate, and when to shut up

The single most useful rule we've adopted:

Validate on blur for errors. Validate on change for recovery.

That's it. When the user is still inside a field, don't tell them they're wrong — they know they're not done. Wait until they leave the field. But once a field has already been marked as invalid, switch to onChange so they see the error clear the instant they fix it. Positive feedback should be immediate; negative feedback should be patient.

There are two exceptions worth building for:

  • Password strength / character rules. Users expect live feedback here, but frame it as a checklist that fills in, not as errors that appear and disappear.
  • Confirm-password fields. Validate on change once the primary password field is valid, because the whole point is real-time matching.

A minimal React hook that gets the timing right

import { useState } from 'react';

export function useFieldValidation(validate: (v: string) => string | null) {
  const [value, setValue] = useState('');
  const [error, setError] = useState<string | null>(null);
  const [touched, setTouched] = useState(false);

  const onChange = (v: string) => {
    setValue(v);
    // Only re-validate live if we've already shown an error
    if (touched && error) setError(validate(v));
  };

  const onBlur = () => {
    setTouched(true);
    setError(validate(value));
  };

  return { value, error, onChange, onBlur, touched };
}

No library required. The touched && error gate is the whole trick — it means the field stays quiet until the user leaves it, then becomes responsive once a real problem exists.

Message content: specific, human, actionable

A red border with the word "Invalid" is worse than no error at all, because it tells the user something is wrong and refuses to say what. Every error message should answer three questions:

  1. What went wrong?
  2. Why does it matter?
  3. What should I do next?

Compare:

  • ❌ "Invalid phone number."

  • ✅ "We need 10 digits including the area code — for delivery updates."

  • ❌ "Password does not meet requirements."

  • ✅ "Add one number or symbol. Everything else looks good."

The second version of each is longer, and that's fine. Error text isn't where you optimise for brevity. It's where you optimise for the user not giving up.

A few writing rules we enforce in review:

  • Never blame the user ("You entered..."). Use passive or first-person plural ("We couldn't...").
  • Never say "error" in the message. The state is already an error; the word is redundant and cold.
  • Never rely on colour alone. Pair red with an icon and text so colour-blind users and grayscale printouts still communicate.

Accessibility: the four attributes that actually matter

Here's the minimum viable accessible error state for a text input. Everything else is polish.

<label for="email">Email address</label>
<input
  id="email"
  name="email"
  type="email"
  aria-invalid="true"
  aria-describedby="email-error"
  autocomplete="email"
/>
<p id="email-error" role="alert">
  This email is already registered. Try signing in instead.
</p>

Four things are doing the work:

  • aria-invalid="true" tells assistive tech the field is in an error state, regardless of the red border.
  • aria-describedby wires the error text to the input, so screen readers announce it when the field gains focus.
  • role="alert" makes the message announce immediately when it appears, not just when focus returns.
  • autocomplete is unrelated to errors but prevents most of them in the first place. Ship it on every field.

On submit failures, move focus to the first invalid field. Do not just scroll — focus. Keyboard users and screen reader users depend on it, and mouse users don't notice either way. It's a free win.

Summary errors on submit

For long forms, a summary block at the top of the form on submit works well, provided:

  • Each item in the summary is a real anchor link to the offending field.
  • The summary itself receives focus after submit.
  • The individual field errors remain in place — the summary is additive, not a replacement.

Visual design: three tokens, not thirty

Most design systems overspecify error states. You do not need separate treatments for warning, error, critical, and destructive. For form validation, three tokens cover 95% of cases:

  • --color-feedback-error — border and icon
  • --color-feedback-error-bg — subtle field background tint, optional
  • --color-feedback-error-text — the message, which must hit AA contrast against the page background, not against the tint

That last point catches teams out constantly. A red message on a pale red background often fails contrast even when the message on white passes. Test the message against the actual background it renders on.

Avoid animating the error in with a shake or a bounce. It reads as punishment. A simple opacity fade over 120–160ms is enough. Save motion for success confirmations, where it earns its keep.

Server errors are different — treat them that way

Client-side validation catches format problems. Server-side validation catches truth: "this email is already taken", "this coupon expired", "this card was declined". These need different UX:

  • Return them attached to the specific field when possible (email field for "already registered").
  • Include a recovery action inside the message ("Sign in" as a link, not just "try signing in").
  • For non-field errors (network, 500s), use a form-level banner that stays visible and doesn't disappear on the next keystroke.

And whatever you do, do not clear the form on a server error. We still see this in the wild. A user fills out 14 fields, hits submit, the API 500s, and everything vanishes. That's not an error state — that's a bug that happens to be styled.

Where we'd start

If you own a form that matters — checkout, signup, application, anything with a conversion number attached — spend an afternoon on this checklist:

  1. Move all onChange validation to onBlur, with onChange recovery once errored.
  2. Rewrite every error message to answer what/why/what next.
  3. Add aria-invalid, aria-describedby, and role="alert" to every field error.
  4. Move focus to the first invalid field on submit.
  5. Verify error text contrast against the actual rendered background.

None of this needs a redesign, a new component library, or a sprint. It's a day of work that will outperform most of the growth experiments queued behind it. If you'd rather have someone else do the audit and the fixes, that's the kind of thing our product engineering team tends to knock out in a week.

#forms#accessibility#ux-patterns#validation#design-systems

Want a team like ours?

72Technologies builds production software for the kind of teams who actually read this blog.

Start a project