Error Summary vs Inline Errors

Should a failed form show its errors next to each field, in a summary at the top, or both? And when both exist, where should focus go after submit? The choice affects how quickly different users find and fix problems: sighted mouse users scan for red, keyboard users follow focus, screen reader users hear what is focused or announced, and users with cognitive disabilities benefit from knowing the scope before they start. This recipe compares the two delivery surfaces on those dimensions, recommends a default — inline errors always, a summary whenever more than one error is possible — and implements the focus rule that switches between them based on the number of errors, all built on the Constraint Validation API and the site’s novalidate plus reportValidity() baseline.

When Each Surface Is Needed

Both surfaces answer different questions, which is why most robust forms use both.

  • Inline errors answer “what is wrong with this field, and how do I fix it?” They are needed on every form, because the user must see the message at the moment they edit the field.
  • An error summary answers “how many problems are there, and where?” It is needed whenever more than one error can occur at once, especially on long forms, forms where errors may be off-screen, and server-rendered forms where no script moves focus.
  • Neither alone is sufficient for longer forms: inline-only makes users hunt; summary-only makes users remember the message while they navigate.

The wider comparison of error surfaces — including toasts and modals, which are usually the wrong choice for field errors — is in inline vs toast vs modal error delivery. The summary component itself is built in building an accessible error summary.

What each surface does for users Two columns comparing inline field errors and an error summary on what they tell users and where they fall short. Inline errors ✓ message at the point of editing ✓ read with the field via aria-describedby ✓ clear live as the user fixes the field ✗ scope unknown on long forms ✗ off-screen errors easy to miss Error summary ✓ count and list in one place ✓ links jump to each field ✓ works after a server-rendered reload ✗ useless without inline messages ✗ extra step for single errors
Inline errors explain; the summary orients. Combined, they cover both the local and the overall question.

Minimal Working Combined Delivery

const form = document.querySelector<HTMLFormElement>("#application")!;
const summary = document.querySelector<HTMLElement>("#error-summary")!;

type Problem = { field: HTMLElement; message: string };

function validateAll(): Problem[] {
  const problems: Problem[] = [];
  const seenGroups = new Set<string>();
  for (const el of form.elements) {
    if (!(el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement)) continue;
    const message = el.willValidate && !el.validity.valid ? messageFor(el) : "";
    renderInline(el, message);                            // inline: every field, always
    if (!message) continue;
    const key = el.type === "radio" || el.type === "checkbox" ? `group:${el.name}` : el.id;
    if (seenGroups.has(key)) continue;
    seenGroups.add(key);
    problems.push({ field: el, message });
  }
  return problems;
}

form.addEventListener("submit", (event) => {
  event.preventDefault();
  const problems = validateAll();
  if (problems.length === 0) {
    summary.hidden = true;
    return form.submit();
  }
  if (problems.length === 1) {
    summary.hidden = true;                                // single error: go straight there
    form.reportValidity();                                // focuses and announces the field
    return;
  }
  renderSummary(problems);                                // several: orient first
  summary.hidden = false;
  summary.focus();
});

The switch at one error is a judgement call that many design systems make: a summary for a single problem adds a step without adding information, while for several problems the count and list save the user from discovering them one by one. Whatever threshold you choose, keep it consistent across the product so users learn what to expect.

Where does focus go after a failed submit? A decision tree that sends focus to the single invalid field when there is one error, and to the error summary when there are several, with inline errors shown in both cases. How many problems after submit? one Focus the field (reportValidity) several Is the form long or partly off-screen? yes Focus the summary no Summary shown; focus summary for consistency
Inline errors are always shown; the number of problems only decides whether focus goes to the field or to the summary.

Delivery Option Reference

Aspect Inline error Error summary
Location Beside or above its field First element in the form
Linked by aria-describedby on the field Links with href="#field-id"
Announced when Field is focused or explored Summary receives focus / alert role
Updates Live as the field is fixed On each submit attempt
Required for Every error Forms where several errors can occur
Works without JS Yes, if server-rendered Yes, if server-rendered
Message text Canonical Identical to the inline text

Verification Steps

import { test, expect } from "@playwright/test";

test("one error focuses the field, several focus the summary", async ({ page }) => {
  await page.goto("/application");
  await fillAllExcept(page, ["Email address"]);
  await page.getByRole("button", { name: "Submit application" }).click();
  await expect(page.getByLabel("Email address")).toBeFocused();
  await expect(page.locator("#error-summary")).toBeHidden();

  await page.getByLabel("Full name").fill("");
  await page.getByRole("button", { name: "Submit application" }).click();
  await expect(page.locator("#error-summary")).toBeFocused();
  await expect(page.locator("#error-summary h2")).toHaveText("There are 2 problems");
});

Edge Cases and Failure Modes

Summary-only designs. Some forms list errors at the top and leave fields unmarked, forcing users to scroll back and forth. Always render inline messages as well.

Inline-only on long forms. Without a summary, a screen reader user hears the first error, fixes it, submits, hears the second, and so on — a loop that can take many round trips on a long form.

Different wording in the two places. Generate both from the same function; different phrasings make users think there are more problems than there are.

Summary that duplicates announcements. If the summary has role="alert" and receives focus and each inline error has role="alert", screen readers may read everything two or three times. Use alert on the summary only, and link inline errors with aria-describedby instead.

Measuring Which Delivery Works

If you are unsure whether a summary helps your forms, measure it. Two metrics are informative: the number of submit attempts before success (a summary should reduce it on long forms, because users fix every problem in one pass) and the time between a failed submit and the next attempt (which should fall when users can see the scope immediately). Compare a variant with the summary against one without, on the same form, and watch abandonment at the failed-submit moment in particular.

Short Forms Need Less

On a two- or three-field form — a sign-in, a newsletter box, a search filter — a summary is usually unnecessary: every field is visible at once, errors are rarely multiple, and reportValidity() taking the user to the first invalid field is enough. The exception is sign-in, where the error is deliberately form-level (“Email or password is incorrect”) and belongs in a single alert region above the fields rather than at either field. Reserve the full summary for forms long enough that users cannot see all fields at once, or for any form whose server-rendered error page must work without script.

Server-Rendered Pages Need the Summary Most

On a page rendered by the server after a failed post, there is no focus management unless you add it, and screen reader users land at the top of a new page. The summary — first in the form, with an “Error:” page title and alert semantics — is what tells them that the submission failed and where to go. That makes the summary non-negotiable for progressively enhanced forms, even if the scripted path uses the single-error shortcut described above. The server-rendered markup is shown in building an accessible error summary, and the no-script flow in progressive enhancement without JavaScript.

Inline Messages for Different Users

Inline messages carry most of the load, so their details matter for every group of users. For sighted users, the message must be close to the field, visually distinct in more than colour, and stable in position so the layout does not jump — see reserving space for error messages to prevent layout shift. For screen reader users, it must be referenced by aria-describedby so it is read with the field, and the field must carry aria-invalid="true" while the message is shown. For keyboard users, it must not insert focusable elements between fields unnecessarily. For users with cognitive disabilities, it must say how to fix the problem in plain words, not just that something is wrong. A summary built on top of inline messages that meet these criteria inherits their quality; a summary cannot compensate for inline messages that miss them.

What each user group needs from error delivery Cards describing what sighted mouse users, keyboard users, screen reader users and users with cognitive disabilities each need from inline errors and summaries. Sighted mouse users messages near fields, visible without colour, stable layout Keyboard users focus moved to the summary or field; links that land with context Screen reader users count announced, messages in aria-describedby, aria-invalid set Cognitive disabilities scope first, plain fix-it wording, same text in both places
The summary orients and the inline message instructs; each user group relies on a different part of that pair.

Frequently Asked Questions

Should I use an error summary or inline errors?

Use inline errors on every form, and add an error summary whenever more than one error can occur at once. They answer different questions: what is wrong with this field, and how many problems are there overall.

Where should focus go after a failed submit?

With one error, move focus directly to that field. With several, move focus to the error summary so users hear the count and list first. Apply the rule consistently across your product.

Should summary items use the same wording as inline errors?

Yes. Identical text lets users recognise the problem when they arrive at the field. Generate both from the same message function.

Can the summary replace inline errors on a long form?

No. Without inline messages, users must remember each message while navigating. The summary is an index to the inline errors, not a substitute for them.

← Back to Inline vs Toast vs Modal Error Delivery