Accessible Forms and Error Recovery

Forms are where users get things done — signing up, paying, booking, applying — and where they most often get stuck. A form is accessible when every person can understand what each field wants, enter it with the input method they use, find out what went wrong when something fails, and fix it without losing their work. Error recovery is the part teams most often neglect: a red border and a toast that disappears after three seconds may look fine to a sighted mouse user and leave a screen reader user with no idea why nothing happened.

Most of what makes a form accessible is plain, well-structured HTML. The rest is deliberate behavior after a failed submission: clear messages, programmatic links between fields and errors, and focus moved to where the user can act.

TL;DR

Quick Example

An email field after a failed submission. The summary at the top has received focus; its link jumps to the field; the field keeps the user's input and exposes its hint, its error, and its invalid state to assistive technology.

A screen reader user hears the summary when it receives focus, follows the link, and on reaching the field hears its label, its hint, the error, and that it is invalid — the same information a sighted user gets from the layout.

Core Concepts

Labels, Hints, and Required Fields

Every control needs a label that is visible and associated with it — label for pointing at the input's id, or the input nested inside the label. The association gives the control its accessible name and makes the label clickable, which helps people with limited dexterity. Placeholder text disappears as soon as someone types, often has poor contrast, and is not a reliable name.

Put format hints ("For example, 15 3 1984") in text before the field, not in a tooltip. Mark required fields in words, or mark optional ones as "(optional)" if most fields are required, and add the required attribute so assistive technology announces it.

Grouping Related Fields

Radio buttons, checkbox groups, and multi-part inputs such as dates or addresses belong in a fieldset with a legend. The legend gives the group a name, so "Yes" and "No" are announced as answers to the actual question.

Input Purpose and Autocomplete

autocomplete tokens — name, email, tel, street-address, postal-code, cc-number, new-password, current-password, one-time-code — let browsers and password managers fill fields accurately. WCAG 1.3.5 (Identify Input Purpose) requires them for fields that collect information about the user. They reduce typing for everyone and are essential for people with motor or memory impairments.

Validation Timing

Writing Error Messages

WCAG 3.3.1 (Error Identification) requires errors to be identified in text; 3.3.3 (Error Suggestion) requires a suggested correction when one is known.

Use the same wording in the summary and next to the field, write in plain language, and never blame the user.

Handling a Failed Submission

The pattern below validates on submit, writes each error next to its field, links fields to their errors, builds a summary, and moves focus to it.

When the summary has done its job and the user corrects a field, remove that field's aria-invalid and error text; when every error is fixed, hide the summary. For very short forms, moving focus straight to the first invalid field can be enough; for anything longer, the summary tells users how many problems there are before they start fixing them. Moving focus is covered in Keyboard Navigation and Focus Management.

Server-Side Errors

Server validation is the source of truth. Return field-level errors in a structured response — for example a 422 with { "errors": [{ "field": "email", "message": "…" }] } — and render them with exactly the same function, so users get the same experience whether the browser or the server caught the problem. Sharing one schema between client and server, for instance with Zod, keeps rules and messages consistent.

Asynchronous Checks

Checks that need a request — "that username is taken" — should report through a polite live region (aria-live="polite") after the user pauses or leaves the field, so the result is announced without stealing focus.

Recovering Without Losing Work

Keep Everything the User Entered

A failed submission, a network error, or an expired session must never wipe the form. Re-render the submitted values, keep drafts of long forms in storage, and after a sign-in timeout return users to the same step with their answers intact. Losing a half-written application is one of the most damaging failures a form can have.

Don't Ask Twice

WCAG 2.2 added 3.3.7 (Redundant Entry): information the user already provided in the same process should be filled in or selectable — "Billing address is the same as delivery address" — rather than typed again.

Make Authentication Accessible

WCAG 2.2 criterion 3.3.8 (Accessible Authentication, Minimum) rules out sign-in steps that depend on memorizing or transcribing information without an alternative. Allow paste into password and code fields, support password managers with correct autocomplete values, and accept one-time-code autofill.

Let People Check Before It Counts

For legal, financial, and data-changing submissions, WCAG 3.3.4 requires that actions are reversible, checked for errors, or confirmed. A review page before final submission, with links back to change each answer, satisfies it and catches mistakes for every user.

Best Practices

Keep Forms Short and Linear

Ask only for what you need, use one column, and put labels above fields. Every extra field is another place for someone to get stuck.

Leave the Submit Button Enabled

A disabled button gives no reason why it is disabled, is often skipped by screen readers, and leaves users guessing. Let people submit and show them exactly what needs fixing.

Choose Input Types Carefully

Use type="email", type="tel", and inputmode="numeric" to bring up suitable keyboards. Avoid type="number" for values that are not quantities — card numbers, ZIP codes, verification codes — because spinners and number parsing get in the way.

Test the Unhappy Paths

Submit forms empty, with wrong formats, offline, and after a session timeout, using only the keyboard and a screen reader. See Accessibility Testing.

Common Mistakes

Placeholder as the Only Label

Errors Shown Only by Color

Clearing the Form After an Error

Errors That Vanish

FAQ

Should I validate inline or on submit?

Both, carefully. Always validate on submit. Add inline validation only after a user has finished with a field — on blur — and use typing events only to clear an error once it is fixed. Premature errors interrupt people, especially screen reader users who hear each one announced.

What does aria-invalid do?

It tells assistive technology that the field's current value failed validation, so screen readers announce "invalid" when the field receives focus. Set it only after validation has run, not on empty fields the user has not reached, and remove it when the value becomes valid.

Should I use aria-describedby or aria-errormessage?

aria-describedby has the broadest support and works for both hints and errors, so it is the safe default. aria-errormessage is designed for error text specifically but has historically had weaker screen reader support; test it with your users' assistive technology before relying on it alone.

How should required fields be marked?

In text. Either label required fields ("Name (required)") or, if nearly every field is required, mark the few optional ones. Add the required attribute so it is announced. An asterisk alone needs an explanation and is easily missed.

Do short forms need an error summary?

Not always. For a one- or two-field form such as a search or sign-in, moving focus to the first invalid field with its message is usually clear enough. Longer forms benefit from a summary that lists every problem with links to the fields.

Related Topics

References