Error Summary
Headless Error Summary; covers WCAG 2.2 error identification, focusable validation summaries.
| Package | @vizejs/ui/error-summary |
| Maturity | stable |
| Own the source | vize lib pull error-summary |
| Requires | id |
| Aliases | form error summary, validation summary, invalid field list |
| Covers | WCAG 2.2 error identification, focusable validation summaries |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { ErrorSummary } from "@vizejs/ui/error-summary";
Or copy the source into your project with vize lib pull error-summary (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Form that lists invalid fields as links above the form after a failed submit. -->
<script setup lang="ts">
import { ref, shallowRef, useId } from "vue";
import { ErrorSummary, type ErrorSummaryField } from "@vizejs/ui/error-summary";
const nameId = useId();
const emailId = useId();
const name = ref("");
const email = ref("");
const errors = shallowRef<readonly ErrorSummaryField[]>([]);
function submit(): void {
const next: ErrorSummaryField[] = [];
if (name.value.trim() === "")
next.push({ id: nameId, label: "Full name", message: "Enter your name" });
if (!email.value.includes("@")) {
next.push({ id: emailId, label: "Email", message: "Enter a valid email address" });
}
errors.value = next;
}
</script>
<template>
<form novalidate @submit.prevent="submit">
<ErrorSummary :fields="errors" heading="There is a problem" />
<label :for="nameId">Full name</label>
<input
:id="nameId"
v-model="name"
autocomplete="name"
:aria-invalid="errors.some((field) => field.id === nameId)"
/>
<label :for="emailId">Email</label>
<input
:id="emailId"
v-model="email"
type="email"
autocomplete="email"
:aria-invalid="errors.some((field) => field.id === emailId)"
/>
<button type="submit">Create account</button>
</form>
</template>
API
ErrorSummary
Source: src/families/form/error-summary/error-summary.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
fields |
readonly ErrorSummaryField[] |
[] |
Invalid fields in document order. Field ids must be unique. |
heading |
string |
"" |
Text for the heading that labels the summary region. |
autoFocus |
boolean |
true |
Move focus into the summary when invalid fields appear. |
Events
| Event | Payload | Description |
|---|---|---|
fieldFocus |
[field: ErrorSummaryField] |
Fires after a summary link moves focus to the named invalid control. |
restore |
[target: HTMLElement | null] |
Fires after a repair settles focus, with the restored element or null. |
Slots
| Slot | Slot props | Description |
|---|---|---|
heading |
{ readonly fields: readonly ErrorSummaryField[] } |
Replaces the heading prop text inside the labelling element. |
field |
{ readonly field: ErrorSummaryField } |
Replaces one link's content; the default is label-prefixed message text. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
hasErrors |
— | |
focusSummary |
— | |
focusField |
— | |
restoreFocus |
— |
Behavior
Normative state × input → outcome table for error-summary.vue
(@vizejs/ui/error-summary). Every row is proven by the named test in
src/families/form/error-summary/error-summary.test.ts or
src/families/form/error-summary/error-summary-ssr.test.ts; compile-only
assertions live in src/families/form/error-summary/error-summary.types.test-d.ts.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| E1 | valid | render | host renders, summary region stays out of the tree | stays out of the tree while every field is valid |
| E2 | valid | fields become invalid | labelled group renders the invalid fields as links | lists invalid fields and takes focus |
| E3 | valid | fields become invalid | summary takes focus and captures the prior element | lists invalid fields and takes focus |
| E4 | invalid | link click | focus moves to the named control, fieldFocus fires |
moves focus to an invalid control from its link |
| E5 | invalid | every field repaired | focus returns to the captured element, restore fires |
restores focus when every field is repaired |
| E6 | invalid | focus moved elsewhere, then repaired | focus is not stolen and the capture is dropped | does not steal focus after a repair |
| E7 | invalid | autoFocus false |
summary renders without moving focus | respects autoFocus false |
| E8 | invalid | focusField unknown id |
returns null and focus stays put | moves focus to an invalid control from its link |
| E9 | any | duplicate field ids | throws a stable option diagnostic | rejects invalid fields options |
| E10 | no scope | useErrorSummary |
throws a stable setup diagnostic | rejects composable use outside an effect scope |
| E11 | disposed | any controller call | throws a stable disposed diagnostic | rejects use after dispose |
| E12 | SSR | render with fields | byte-identical labelled group and anchor list | SSR test |
| E13 | public types | malformed field | compilation rejects misuse | src/families/form/error-summary/error-summary.types.test-d.ts |