// preview in
saved for every page

Error summary

Lists the invalid fields at the top of a form after a failed submit, each one a link that moves focus to its control. Use it for forms with more than a couple of fields.

Pattern Alert + in-page linksClient componentSource ↗

Installation

Set up Camaleo once (Installation), then import it. It goes with the Field parts.

import { ErrorSummary } from "@camaleo/react";

Anatomy

A danger Alert with a title and a list of links. Every part has a data-slot you can target from CSS.

icontitleEmailPhone
[data-slot="alert"][data-kind="error-summary"][data-slot="alert-icon"] · [data-slot="alert-title"] · [data-slot="error-summary-list"][data-slot="error-summary-link"]

Examples

Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.

minimal · light · #3fa34d
Enter the full address, like ana.souza@acme.com.
Enter 10 digits, like (415) 555-0132.

Focus on submit

Press Continue with empty fields. The summary takes focus through ref and tabIndex={-1}, so it is read first, and it shrinks as you fix each field.

Errors from the server

Press Save. When your API rejects a value, show the same summary: people get one place to look whether the error came from the browser or the backend.

Fix 2 fields to continue.

Choose a plan to see the price.
Accept the terms to create the workspace.

Selects and checkboxes

target is the id of any focusable control. A checkbox's id lands on its hidden input, which hands focus to the visible box.

Account
Enter the full address, like ana.souza@acme.com.
Billing address
Enter a 5-digit ZIP code.

In a long form

Click a link. The control scrolls to the middle of the screen, so a sticky header never hides the field you jumped to.

One or many

Count the errors in the title and pick the wording with Intl.PluralRules, so "1 field" and "3 fields" both read right in every language.

Error

Fix 1 field to continue.

With a written label

label adds a word like "Error". Some skins show it in place of the icon, and it gives the alert a text cue besides color.

In context

Error summaries sit at the top of real forms. These are whole pieces of UI you can copy, in the skin you picked.

Create your account

Free for 14 days. No card needed.

Signup with react-hook-form + zodCard · ErrorSummary · Field · Input · PasswordInput · Checkbox

Payment

Total due today: $288.00

Enter all 16 digits.
This card expired in September 2024.
Card paymentCard · ErrorSummary · Field · Input · Button

Public profile

Your changes were not saved.

“ana.souza” is taken. Try ana.souza.design.
Start with https://, like https://anasouza.design.
Profile not savedCard · ErrorSummary · Field · Input · Textarea

With react-hook-form and zod

Camaleo has no dependency on a form library. The signup form above wires react-hook-form and zod to Field and ErrorSummary; its Code tab has the whole file.

pnpm add react-hook-form zod @hookform/resolvers
const { control, handleSubmit, formState: { errors, isSubmitted, submitCount } } =
	useForm<Values>({ resolver: zodResolver(schema), defaultValues, shouldFocusError: false });
const invalid = (Object.keys(labels) as (keyof Values)[]).filter((name) => errors[name]);

useEffect(() => {
	if (submitCount > 0) summaryRef.current?.focus();
}, [submitCount]);

{isSubmitted && invalid.length > 0 && (
	<ErrorSummary
		ref={summaryRef}
		tabIndex={-1}
		title={`Fix ${invalid.length} fields to continue.`}
		errors={invalid.map((name) => ({ target: ids[name], message: labels[name] }))}
	/>
)}

Guidelines

DoLink every error to its field.

One click or Enter takes people straight to what needs fixing.

There are errors in the form.

Don’tAn alert that only says something is wrong.

People have to hunt through the form for the red fields.

DoName the field and the fix.

"Card number: enter all 16 digits" works out of context, in a list of links.

Fix 2 fields to continue.

Don’tThe same vague message twice.

Two links called "Invalid value" can't be told apart by a screen reader user.

Props

PropTypeDefaultDescription
labelReactNode—Short category text, like "Notice". Some skins show it next to the icon or instead of it.
iconReactNode—Replaces the danger icon.
title*ReactNode—The heading of the summary; say how many fields need fixing.
errors*{ target: string; message: ReactNode }[]—target is the id of the invalid control; its link moves focus there.

The other div attributes pass through, including ref and tabIndex: use them to move focus to the summary after a failed submit.

Styling hooks

Restyle it without forking: target the attributes, or set the tokens on a parent, a skin or the summary itself.

Attributes

data-slot"alert" · "alert-title" · "error-summary-list" · "error-summary-link"
data-kind"error-summary"
data-tone"danger"

Tokens it reads

Color

--cml-fg--cml-focus-offset--cml-focus-ring--cml-focus-width--cml-surface-raised

Shape & size

--cml-border-width--cml-font-size-help--cml-radius-popover

Type & motion

--cml-tone-line

This component only

--cml-corner-shape--cml-tone--cml-tone-soft
/* one error per line, only in the checkout */
.checkout [data-slot="error-summary-list"] {
  flex-direction: column;
}

Accessibility

TabMoves through the links, in the order of the form.
EnterFollows a link: focus moves to the control and it scrolls into view.
Shift + TabFrom the control, back to the summary's links.
Was this page helpful? · Edit this page on GitHub