Engrain by ByteRush

RCT.10.02 Mid-level· 11 minCore — asked often

Accessible, resilient forms: errors everyone can find and fix

Label every field and link its hints and errors with aria-describedby, show an error summary and move focus to it, choose when to validate, why not to disable the submit button, prevent double submission, and keep forms working without JavaScript.

Comes up in: React, Machine coding, Web fundamentals

Learn#

Rekha uses a screen reader. She fills a government form, presses Submit, and nothing seems to happen. Two fields turned red at the top of a long page, but no one told her screen reader.

An error summary is a box at the top of the form listing every problem, each linking to its field. When submit fails, move focus to it, so screen readers announce it and keyboard users start there:

<div id="error-summary">
  <h2 tabindex="-1">There are 2 problems</h2>
  <a href="#email">Enter your email</a>
  <a href="#phone">Enter a 10-digit mobile number</a>
</div>
<label for="email">Email</label>
<input id="email" name="email" aria-invalid="true" aria-describedby="email-hint email-error">
<p id="email-hint">We send the receipt here.</p>
<p id="email-error">Enter your email</p>

Every field needs a visible <label>. Hints and errors are linked with aria-describedby, so they are read along with the label.

A failed submit, announced and fixableStep 1 of 4

Page

  • (silence)

A screen-reader user submits the form with two mistakes. Without help, nothing is announced; they do not know it failed.

Read the steps as text
  1. A screen-reader user submits the form with two mistakes. Without help, nothing is announced; they do not know it failed.
  2. With an error summary: a box at the top lists both problems, and focus moves to its heading, which is read aloud.
  3. Each item is a link to its field. Activating one moves focus to that input.
  4. The input has aria-invalid="true" and aria-describedby pointing at its error text, so the error is read with the label.

Predict#

Predict first

The submit button stays disabled until the form is valid. What is the problem?

Interview angle#

Say it like this — about 30 seconds

“Each field has a visible label, with hints and errors linked by aria-describedby and aria-invalid on invalid fields. I validate on submit, and after a field is touched. On a failed submit I show an error summary at the top, with links to each field, and move focus to it. I do not disable the submit button for invalid forms. To prevent double submission, I disable or ignore clicks only while a request is pending, and the server uses an idempotency key.”

Stays on your device. Nothing is uploaded.
Follow-up How do you prevent double submission?
Ignore or disable while pending (useFormStatus or a pending flag), share the in-flight request, and use an idempotency key on the server.
Follow-up When should validation run?
On submit and on blur after the user has interacted; while typing, only clear errors once fixed.
Follow-up What is progressive enhancement for forms?
A form that posts to the server and works without JavaScript, enhanced with client validation and optimistic UI when JavaScript loads.

Practice#

Guided

Write showErrors: set field states, keep aria-describedby correct, and build a focused error summary.

Done when

  • Invalid fields get aria-invalid and their error id in aria-describedby, keeping hint ids.
  • Fixed fields lose both, and their error text is cleared.
  • The summary has a focused heading with the right count and a link per error, in form order.
Your solution
Ctrl/⌘ + Enter

Remember#

Try to answer each card before you open it. Then add them to your reviews.

What links an error message to its input?

aria-describedby with the error element's id.

Where should focus go after a failed submit?

To the error summary at the top of the form.

Why not disable submit until the form is valid?

Users get no reason, and the button may not be reachable.

Two ways to prevent double submission?

Ignore clicks while pending, and an idempotency key on the server.

Go deeper

Four layers. Stop at the one your interview needs; each ends with a quick check.

The core ideaEvery round

Placeholders are not labels: they disappear while typing and have low contrast. Keep hints visible and linked with aria-describedby.

Check yourself: Is placeholder text a label?

No. It disappears while typing and is often not read as a label.

How it works underneathSenior rounds

tabindex="-1" lets script focus the heading without adding it to the Tab order. The GOV.UK Design System pattern, widely copied, puts the summary first and links each error Source: GOV.UK Design System: Error summary.

Check yourself: Why give the summary heading tabindex=-1?

So it can receive focus from script without becoming a tab stop.

Edge cases and trapsWhere people slip
Validate on submit and on blur after interaction; while typing, only remove errors once fixed.
Do not disable submit for invalid forms; disable only while a submission is pending.
Pair that with an idempotency key, because double requests also come from retries.
Check yourself: When should inline errors appear?

After submit or after the user leaves the field, not on the first keystroke.

In productionStaff-level answers

Progressive enhancement: a form that works as plain HTML posting to the server, enhanced by JavaScript. React 19 form actions with server functions support this.
WCAG 3.3.1 requires errors identified in text Source: WCAG 2.2: Understanding Error Identification.

Check yourself: Which WCAG criterion covers identifying errors in text?

3.3.1 Error Identification.

Finished the five steps? Mark it, and its cards join your reviews.

Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

Interview questions this answers

  • How do you prevent double submission?