npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ngx-error-summary

v0.2.1

Published

Accessible validation error summary for Angular reactive forms. Signal-based, zoneless, standalone — one message pipeline shared by the summary block and inline field errors.

Readme

ngx-error-summary

Accessible validation error summary for Angular reactive forms.

npm downloads CI Angular license

Signal-based, zoneless-safe, standalone. The summary block and the inline field messages resolve through one pipeline, so they can't drift out of sync — which is the failure mode that makes most hand-rolled validation displays inaccessible.

Live demo →

npm install ngx-error-summary

What it does

  • Summary on submit that takes focus, so screen reader users are told the submission failed instead of being left on the button.
  • Each entry is a link that moves focus to the offending field — including fields nested in groups and FormArrays.
  • Inline messages share the same pipeline, so the wording in the summary and beside the field can never disagree.
  • Cross-field errors surface against the group rather than vanishing.
  • Themeable through CSS custom properties, and the heading level is yours to choose so the page outline stays intact.
  • No dependencies beyond Angular itself, ~14 kB packed, tree-shakeable.

Why

Angular ships validators but no story for presenting failures accessibly. Teams end up hand-writing a summary block, hand-writing inline messages, and letting the two disagree. Screen reader users then get a summary that says one thing and a field that says another — or no announcement at all, because focus never moved.

This library implements the established pattern (summary on submit, focus moves to it, each entry links to its field) as a signal-driven primitive.

Usage

Register your copy once:

import { provideErrorSummary } from 'ngx-error-summary';

bootstrapApplication(App, {
  providers: [
    provideErrorSummary({
      heading: 'There is a problem',
      messages: {
        required: ({ label }) => `Enter ${label.toLowerCase()}`,
        productCode: () => 'Product code must look like AB-1234',
      },
    }),
  ],
});

Then in the form:

<form [formGroup]="form" (ngSubmit)="submit()">
  <nes-error-summary [form]="form" [show]="submitted()" />

  <label for="email">Email address</label>
  <input id="email" formControlName="email" />
  <span nesErrorFor="email" nesLabel="Email address"></span>

  <button type="submit">Continue</button>
</form>
protected readonly submitted = signal(false);

protected submit(): void {
  this.submitted.set(true);
  if (this.form.invalid) return;   // summary appears and takes focus
  // ...
}

How fields are located

Summary entries move focus to the offending control. Resolution is convention-based, so nothing needs registering — the first match wins:

  1. [id] equal to the control path (address.postalCode)
  2. [data-nes-field] equal to the control path
  3. [formControlName] equal to the leaf segment

Styling

Every painted value is a CSS custom property, so you restyle the summary from anywhere in your app — no ::ng-deep, no !important, no specificity fight:

:root {
  --nes-summary-color: #b00020;
  --nes-summary-border-color: #b00020;
  --nes-summary-background: #fff5f5;
  --nes-summary-padding: 1.5rem;
}

Scope them like any other custom property — set them on :root for the whole app, or on a wrapper to restyle one form.

| Custom property | Default | Applies to | | ----------------------------- | -------------- | ------------------------------ | | --nes-summary-color | inherit | Text colour of the whole block | | --nes-summary-background | transparent | Block background | | --nes-summary-border-width | 4px | Border thickness | | --nes-summary-border-color | currentColor | Border colour | | --nes-summary-border-radius | 0 | Corner radius | | --nes-summary-padding | 1rem | Inner padding | | --nes-summary-gap | 1.5rem | Space below the block | | --nes-summary-heading-size | 1.125rem | Heading font size | | --nes-summary-heading-color | inherit | Heading colour | | --nes-summary-list-indent | 1.25rem | List indentation | | --nes-summary-link-color | inherit | Entry link colour | | --nes-summary-focus-width | 3px | Focus ring thickness | | --nes-summary-focus-color | currentColor | Focus ring colour | | --nes-summary-focus-offset | 2px | Focus ring offset |

Custom properties are used rather than plain class overrides because the component uses emulated encapsulation: Angular rewrites .nes-summary to .nes-summary[_ngcontent-xyz], which outranks a consumer's own .nes-summary rule. Custom properties inherit straight through that.

If you'd rather restyle from scratch, the markup carries stable class names — .nes-summary, .nes-summary__heading, .nes-summary__list, .nes-summary__link — and the host element gets [data-empty] when there is nothing to report.

Heading level

The summary renders a real heading element. Which level is right depends on the page, not on the library — a summary inside a section already introduced by an h2 belongs at h3, or it breaks the document outline that screen reader users navigate by.

<nes-error-summary [form]="form" [show]="submitted()" [headingLevel]="3" />

Set the app-wide default through the provider, and override it per instance where a page needs something different:

provideErrorSummary({ headingLevel: 3 });

Levels 1–6 render <h1>–<h6>; the default is 2. Real heading elements are used rather than role="heading" with aria-level, because native semantics have better assistive-technology support.

API

| Export | Purpose | | ------------------------------------------------------------------------------------------- | --------------------------------------------------------- | | <nes-error-summary [form] [show] [heading] [headingLevel] [includeUntouched] (navigated)> | The summary block | | [nesErrorFor] + [nesLabel] [nesEager] | Inline message for one control | | provideErrorSummary(config) | App-wide messages, resolvers, heading, heading level | | collectErrors(control, config, opts) | The walker, if you want to render your own UI | | FieldError | { path, key, message, error } — the shared protocol | | ErrorMessageResolver | (ctx) => string \| null, chained before the message map | | HeadingLevel | 1 \| 2 \| 3 \| 4 \| 5 \| 6 |

Nested groups and FormArrays are walked depth-first in declaration order, so the summary lists problems in the order they appear on screen. Group-level (cross-field) errors are reported against the group's own path rather than being dropped. Disabled subtrees are skipped, matching Angular's own validation.

Licence

MIT