skip to content

An Angular component adds NO_ERRORS_SCHEMA to its schemas to silence template errors; why is that risky, and when is CUSTOM_ELEMENTS_SCHEMA the right choice?

level: seniorimportance: should knowfreq 34%

answer

  1. switching off the DOM schema check
  2. the hyphen in custom element names
  3. typos and forgotten imports compile
  4. scope it to one wrapper

basics

~10 s

NO_ERRORS_SCHEMA turns off Angular's unknown-element and unknown-property checks, so typos and forgotten imports compile and render nothing. CUSTOM_ELEMENTS_SCHEMA only allows dash-named unknown elements and their properties, for hosting real Web Components.

solid answer

~40 s

The compiler checks templates against a DOM schema and reports NG8001 for unknown elements and NG8002 for unknown property bindings. `NO_ERRORS_SCHEMA` disables both checks for that component, so `<app-user-crad>`, a missing `imports` entry or a misspelled input all compile and fail silently as blank UI. `CUSTOM_ELEMENTS_SCHEMA` is narrower: it allows unknown elements whose names contain a dash, and any property on them, which is what a real Web Component needs; unknown dash-less tags and bad properties on standard elements still error. It still hides a forgotten dash-named Angular component, so apply it only on the small wrapper component that renders the custom element. On `@Component`, `schemas` is valid only for standalone components.

code

ts · 11 lines
ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import './vendor/chart-element.js'; // registers <vendor-chart>

@Component({
  selector: 'app-sales-panel',
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `<vendor-chart [series]="series" />`,
})
export class SalesPanel {
  series = [3, 5, 8];
}

go deeper

for a junior

Know that Angular errors on unknown elements, and that CUSTOM_ELEMENTS_SCHEMA exists for Web Components with dashed tag names.

for a middle

Explain the table: which unknown elements and properties each schema allows, and why the dash in the tag name decides it.

for a senior

Diagnose a blank component caused by a schema hiding a missing import, and confine CUSTOM_ELEMENTS_SCHEMA to the one wrapper hosting the Web Component.

for a principal

Set a codebase policy: no NO_ERRORS_SCHEMA in application code, a lint or review rule for schemas, and wrappers as the only place custom elements enter.

## Why Angular complains about unknown elements at all The Angular compiler type-checks every template against a **DOM schema**: a registry of the standard HTML elements and their properties. When a tag is neither a standard element nor the selector of an imported component, it reports **NG8001**, *is not a known element*. When a `[property]` binding names something that is neither a DOM property nor an input of a matched directive, it reports **NG8002**, *Can't bind to 'x' since it isn't a known property*. Those errors are how Angular catches typos and forgotten `imports` at build time. A component's `schemas` field relaxes the check, and there are two schema constants in `@angular/core`. ## The two schemas | | `CUSTOM_ELEMENTS_SCHEMA` | `NO_ERRORS_SCHEMA` | |---|---|---| | Unknown element **with a dash** (`<my-chart>`) | allowed | allowed | | Unknown element **without a dash** (`<chart>`) | still an error | allowed | | Any property bound on a dash-named element | allowed | allowed | | Unknown property on a standard element (`<div [foo]>`) | still an error | allowed | | Intended use | real Web Components | almost never | The dash matters because the HTML specification requires custom element names to contain a hyphen. `CUSTOM_ELEMENTS_SCHEMA` says: *elements that look like custom elements may exist, and I cannot know their properties at compile time*. `NO_ERRORS_SCHEMA` says: *stop checking elements and properties at all*. For a standalone component the field lives on `@Component`: ```ts import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import './vendor/chart-element.js'; // registers <vendor-chart> @Component({ selector: 'app-sales-panel', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<vendor-chart [series]="series" />`, }) export class SalesPanel { series = [3, 5, 8]; } ``` `schemas` on `@Component` is only valid for a standalone component; a component with `standalone: false` gets its schemas from its NgModule, and putting `schemas` on it is a compile error. ## What goes wrong with NO_ERRORS_SCHEMA A team adds `NO_ERRORS_SCHEMA` to make a noisy template compile. From then on, in that component: 1. **Typos compile.** `<app-user-crad>` is treated as an unknown element and rendered as an empty, inert tag. 2. **Forgotten imports compile.** If `UserCard` is missing from `imports`, `<app-user-card>` silently renders nothing. 3. **Input bindings stop being checked.** `[usr]="u"` on a real component, or `[foo]` on a `<div>`, is no longer reported. 4. **Nothing fails in CI.** The bug surfaces as a blank area on screen, found by a user or a screenshot test. The Angular source documents exactly this: using it is *generally discouraged because it prevents useful validation and may hide real errors*, and it recommends `CUSTOM_ELEMENTS_SCHEMA` instead. ## CUSTOM_ELEMENTS_SCHEMA still has a blind spot `CUSTOM_ELEMENTS_SCHEMA` is narrower but not free. Angular component selectors conventionally contain a dash (`app-user-card`), so under this schema a **forgotten import or a typo in a dash-named Angular component** also compiles, because the compiler cannot tell it from a Web Component. Binding inputs on it is not checked either. That is why the schema should be applied **only on the component that actually hosts Web Components**, not copied into every component or a shared base. Standalone components make this easy: the schema is per component, so a small wrapper such as `SalesPanel` above can carry it while the rest of the app keeps full checking. ## A practical policy - Default: **no schema**. Let NG8001 and NG8002 catch mistakes. - Hosting a real custom element: `CUSTOM_ELEMENTS_SCHEMA`, on the smallest wrapper component that renders it. - `NO_ERRORS_SCHEMA` in application code: treat it as a defect to remove; fix the underlying import or binding instead. - When reviewing a component that "renders nothing", check its `schemas` first: a schema is the usual reason a missing import did not fail the build. ## Where this fits This question is about the component-level field. Packaging an Angular component **as** a custom element for use outside Angular is a different topic, and so is the NgModule-level `schemas` field that older, module-based code uses. The compile-time model is the same in both places: schemas only switch off validation, they never make Angular aware of an element's real properties.

  • Does CUSTOM_ELEMENTS_SCHEMA stop Angular from checking a <div [foo]> binding?
    No. It only relaxes the checks for elements whose tag contains a dash. An unknown property bound on a standard element such as `div` is still NG8002, and an unknown tag without a dash is still NG8001. Only `NO_ERRORS_SCHEMA` silences those.
  • Why can't you put schemas on a component marked standalone: false?
    For a component declared in an NgModule, the compiler takes the template's schemas from that module, and it rejects the field on the component with 'schemas' is only valid on a component that is standalone. Standalone components own their schemas, which is what lets you confine one to a single wrapper.

CUSTOM_ELEMENTS_SCHEMA is a spell-checker told to skip every hyphenated word, because they might be brand names; NO_ERRORS_SCHEMA switches the spell-checker off entirely. Both let a real typo through if it happens to fall in the skipped set.

saying these in an interview costs you the question

  • CUSTOM_ELEMENTS_SCHEMA and NO_ERRORS_SCHEMA are two names for the same check.
  • CUSTOM_ELEMENTS_SCHEMA is required to use any Angular component in a template.
  • NO_ERRORS_SCHEMA is a safe way to make a noisy template compile.
  • CUSTOM_ELEMENTS_SCHEMA still catches a forgotten import of a dash-named Angular component.
  • A schema tells Angular which properties the custom element really has.