Skip to content

Error Summary

Form InfrastructureSince 0.2.0
import { ErrorSummary } from '@absoluteui/components/error-summary';
import '@absoluteui/components/error-summary/style.css';

Using Angular? See Error Summary for Angular.

Accessible error navigation. ErrorSummary lists every error after a failed submission as links that move focus to the offending field, following the GOV.UK error summary pattern. The summary is a labelled region that receives focus when shown, so screen readers announce its heading and contents once, without a live region. Form creates and fills one automatically; it can also be used on its own, for example for server-rendered errors.

  • After a failed form submission, to list all errors at the top of the form with links to each field.
  • Showing server-side errors after a full page load, including errors that belong to no field.
  • Inline messages next to a field: controls render those; for other controls use Validation Message.
  • Non-error notices or confirmations: use an alert or toast.
  • Forms managed by Form: it creates and fills the summary itself (use Form’s summaryHost to place it).

Standalone summary linking to invalid fields

Section titled “Standalone summary linking to invalid fields”
import { ErrorSummary } from '@absoluteui/components/error-summary';
import { TextInput } from '@absoluteui/components/text-input';
const summaryHost = document.createElement('div');
const nameHost = document.createElement('div');
document.body.append(summaryHost, nameHost);
const name = TextInput.create(nameHost, { label: 'Full name' }).input;
const summary = ErrorSummary.create(summaryHost);
// Focus moves to the summary; each linked item focuses its target on activation.
summary.show([
{ message: 'Enter your full name', target: name },
{ message: 'The service is busy. Try again in a few minutes.' },
]);

Summary for AbsoluteUI controls with a custom focus callback

Section titled “Summary for AbsoluteUI controls with a custom focus callback”
import { ErrorSummary, type ErrorSummaryNavigateDetail } from '@absoluteui/components/error-summary';
import { TextInput } from '@absoluteui/components/text-input';
const summaryHost = document.createElement('div');
const emailHost = document.createElement('div');
document.body.append(summaryHost, emailHost);
const email = TextInput.create(emailHost, { label: 'Email', name: 'email', type: 'email', required: true });
const summary = ErrorSummary.create(summaryHost, { headingLevel: 3, description: 'Fix the following to continue.' });
if (!email.reportValidity()) {
summary.show([{ message: email.validity.message, target: email.host, focus: () => { email.focus(); } }]);
}
summaryHost.addEventListener('aui-navigate', (event) => {
const { index } = (event as CustomEvent<ErrorSummaryNavigateDetail>).detail;
summaryHost.dataset.lastNavigated = String(index);
});

Heading level for pages whose form sits under an h2 (Mode B)

Section titled “Heading level for pages whose form sits under an h2 (Mode B)”
import { configureAbsoluteUI } from '@absoluteui/core';
import '@absoluteui/components/error-summary';
configureAbsoluteUI({ components: { errorSummary: { headingLevel: 3 } } });
import { ErrorSummary } from '@absoluteui/components/error-summary';
import '@absoluteui/components/error-summary/style.css';
// Errors rendered by the server into data attributes on each invalid input.
const host = document.querySelector<HTMLElement>('#error-summary');
if (host) {
const invalid = Array.from(document.querySelectorAll<HTMLInputElement>('input[data-error]'));
const summary = ErrorSummary.create(host);
summary.show(invalid.map((input) => ({ message: input.dataset.error ?? '', target: input })).filter((item) => item.message !== ''));
}
import { ErrorSummary } from '@absoluteui/components/error-summary';
const summaryHost = document.createElement('div');
const name = document.createElement('input');
name.id = 'full-name';
document.body.append(summaryHost, name);
ErrorSummary.create(summaryHost).show([{ message: 'Enter your full name', target: name }]);
<div class="aui-error-summary" role="region" aria-labelledby=title tabindex="-1" hidden> (consumer host)
<h2 class="aui-error-summary__title" id=title>
<svg class="aui-error-summary__icon aui-icon" aria-hidden="true"><span>There is a problem</span>
</h2>
<p class="aui-error-summary__description"> (optional)
<ul class="aui-error-summary__list">
<li><a class="aui-error-summary__link" href="#field-id">Message</a></li> (item with target)
<li>Message</li> (item without target)
</ul>
</div>
ErrorSummary.create(host: HTMLElement, options?: ErrorSummaryOptions): ErrorSummary

Global configuration key: errorSummary (see Configuration).

Option Type Default Description
title string Heading text. Default: localized “There is a problem”.
description string Optional text under the heading.
icons Partial<{ error: IconRef }> Heading icon override.
headingLevel 2 | 3 | 4 2 Native heading element level. Globally configurable.
focusOnShow boolean true Move focus to the summary when show() displays items. show(items, { focus }) overrides it per call. Globally configurable.
locale string "en-US" Locale of the default heading. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer host; becomes the summary region.
items readonly ErrorSummaryItem[] Items currently shown.
isVisible boolean True while items are shown.
Method Description
show(items: readonly ErrorSummaryItem[], options?: { focus?: boolean }): void Replace the items and show the summary (an empty list hides it). Focuses and scrolls to the summary unless focus is false or focusOnShow is false. Throws on an empty message before changing anything.
clear(): void Remove all items and hide the summary without moving focus.
update(options: Partial<ErrorSummaryOptions>): void Merge options and re-render the heading and description. Invalid options leave the previous options in place.
destroy(): void Remove rendering, attributes, class, and the configuration subscription; unhide the host.
Event Detail Bubbles Cancelable Description
aui-navigate { index: number; message: string } Yes No A summary link was activated and focus moved to its target.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-error-summary Host (role=region, tabindex=-1).
aui-error-summary__title Heading (h2, h3, or h4).
aui-error-summary__icon Heading icon.
aui-error-summary__description Optional text under the heading.
aui-error-summary__list List of errors.
aui-error-summary__link Link to a field; items without a target are plain list text.
Slot Default icon
error alert-circle

Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.

--aui-border-width | --aui-border-width-strong | --aui-color-error-fg | --aui-color-error-subtle | --aui-color-focus-ring | --aui-color-text | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-weight-medium | --aui-font-weight-semibold | --aui-icon-size-md | --aui-line-height-normal | --aui-line-height-tight | --aui-radius-md | --aui-space-1 | --aui-space-2 | --aui-space-4 | --aui-space-5

type HeadingLevel = 2 | 3 | 4;
interface ErrorSummaryItem { readonly message: string; readonly target?: HTMLElement | null; readonly focus?: () => void }
interface ErrorSummaryIcons { error: IconRef }
interface ErrorSummaryOptions { title?: string; description?: string; headingLevel?: HeadingLevel; focusOnShow?: boolean; locale?: string; icons?: Partial<ErrorSummaryIcons> }
interface ErrorSummaryNavigateDetail { index: number; message: string }

Pattern: GOV.UK error summary: a focusable, labelled region with a native heading and a list of in-page links to the invalid fields.

Key Context Action
Tab / Shift+Tab Summary or a link focused Moves between the summary links and the rest of the page.
Enter Link focused Focuses the target (or calls its focus callback), scrolls it to the center, and emits aui-navigate.
Element Role
host region
heading heading (native h2, h3, or h4)
list list (native ul)
item with target link (native a href=#id)
icon none (aria-hidden=true)
Attribute Element Value
aria-labelledby host Heading id
tabindex host -1, so the summary can receive programmatic focus without entering the Tab order

show() moves focus to the summary and scrolls it into view (focusOnShow). The host shows the shared focus ring on :focus-visible; links have their own 1px-offset focus ring.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
2.1.1 Keyboard A
2.4.3 Focus Order A
2.4.6 Headings and Labels AA
2.4.7 Focus Visible AA
3.3.1 Error Identification A
3.3.3 Error Suggestion AA
4.1.2 Name, Role, Value A

Automated test results: Accessibility and compliance.

  • Focusing the summary makes screen readers announce the region name (the heading) and its contents once; no live region is used.
  • Link activation prevents the fragment navigation, so the URL hash does not change and focus lands on the control, not its container.
  • Links are underlined and error-colored; color is not the only cue.
  • Choose headingLevel to fit the page’s heading outline.
  • Announcement on focus has not yet been manually verified with NVDA, JAWS, and VoiceOver.

hidden, visible, focused (:focus-visible ring), link hover (thicker underline), link focus-visible.

From Trigger To
hidden show(items) with items visible; focused unless disabled
visible show(items) items replaced; focused again unless disabled
visible show([]) or clear() hidden; focus unchanged
visible link activated focus on target; aui-navigate
  • Focus moves to the summary when shown, so its name and contents are read once; no live region is used.
  • Link activation prevents the fragment navigation: the URL hash does not change and focus lands on the control itself.
  • Links are underlined and error-colored; color is not the only cue (WCAG 1.4.1).
  • Choose headingLevel to fit the page’s heading outline.
  • Manual screen-reader verification is pending (see Known limitations).

Block-level grid filling its container; long messages wrap.

Logical properties: the thick border sits on the inline-start side and the list indents from the inline start.

Error-subtle background with error-foreground border, heading, and links, from role tokens only. Forced-colors mode uses CanvasText for the border and LinkText for links.

configureAbsoluteUI({ components: { errorSummary: { headingLevel: 3 } } });

Changes apply to existing instances immediately. Form passes focus explicitly (from its focusOnInvalid option), so focusOnShow does not affect Form-owned summaries.

Instance options override global and theme defaults. All options are dynamic.

icons.error (default alert-circle), shown in the heading.

Heading, description, and messages use textContent and are never parsed as HTML. Link hrefs are only #id fragments of the targets. CSP-safe.

No persistence, network access, or telemetry.

One configuration subscription and one click listener per link. show() rebuilds the list. No dedicated performance budget.

Unit tests cover up to four items. A summary is meant for the errors of one form; dozens of items render without issue but become hard to use.

  • An item with an empty or non-string message throws ERROR_SUMMARY_ITEM before anything changes.
  • A target without an id receives a generated one.
  • show([]) hides the summary and never moves focus.
  • Calling show() again while visible re-focuses the summary (unless focus: false).
Code Category Cause
ERROR_SUMMARY_ITEM configuration An item’s message is empty or not a string.
CFG_INVALID_VALUE configuration headingLevel is not 2, 3, or 4, or focusOnShow is not a boolean.

Invalid options at construction throw before rendering; in update() they leave the previous options in place.

  • A target without an id is modified (an id is generated).
  • Without script, the #id link only scrolls; focus lands on the target only if it is focusable.
  • Heading levels are limited to 2, 3, and 4.
  • Announcement on focus has not yet been manually verified with NVDA, JAWS, and VoiceOver.
  • HTML or custom content in items.
  • Item actions other than navigation.