# Angular

A custom element built with elements-kit is just `class extends HTMLElement` registered via `customElements.define`. Angular renders it fine at runtime, but you need to tell the Angular compiler to allow unknown elements and annotate element references with the class to get typed access in TypeScript.

```ts
// shared element — built once, used anywhere
import { defineElement } from "elements-kit/custom-elements";
import { reactive } from "elements-kit/signals";

export class XCounter extends HTMLElement {
  @reactive() count = 0;
}

defineElement("x-counter", XCounter);
```

## Allow custom elements in the template compiler

By default Angular raises an error for unknown element tags. Add `CUSTOM_ELEMENTS_SCHEMA` to the `schemas` array of the component (standalone) or NgModule that uses the element:

```ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";

@Component({
  selector: "app-root",
  template: `<x-counter [count]="5"></x-counter>`,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppComponent {}
```

`CUSTOM_ELEMENTS_SCHEMA` suppresses the unknown-element error for any tag that contains a hyphen. You only need it on the module or standalone component that contains the custom element — not globally.

## Source the prop shape from the class

Three type helpers exported from `elements-kit/custom-elements` read the raw platform surface off the class. All three take the constructor (`typeof XCounter`); `PropertiesOf` also accepts an instance type.

| Helper                          | Shape                                                                                                            | Use when                                                                                 |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `PropertiesOf<typeof XCounter>` | Public instance fields, all optional — the `HTMLElement` surface is dropped; `Slot`-typed fields accept a `Node` | Typing property assignments — prop bindings, wrappers, helper functions                  |
| `AttributesOf<typeof XCounter>` | Every key of `static [ATTRIBUTES]`, valued `string \| null`                                                      | Typing the raw HTML attribute surface — `setAttribute`, server-rendered markup           |
| `EventsOf<typeof XCounter>`     | The `static events` map, verbatim — event name to event type                                                     | Typing `addEventListener`, framework event bindings, `HTMLElementEventMap` augmentations |

## Type element references

Angular doesn't autocomplete custom element props in templates — `CUSTOM_ELEMENTS_SCHEMA` is a runtime-only allowlist. To get typed access to an instance, annotate `@ViewChild` or `document.querySelector` with the class directly:

```ts
import { Component, ViewChild, ElementRef, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { XCounter } from "./x-counter";

@Component({
  selector: "app-root",
  template: `<x-counter #counter [count]="5"></x-counter>`,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppComponent {
  // ref is typed as XCounter — access reactive fields directly
  @ViewChild("counter") counter!: ElementRef<XCounter>;

  increment() {
    this.counter.nativeElement.count++;
  }
}
```

`PropertiesOf<typeof XCounter>` is useful as a type annotation when you need to describe the settable surface of the element — for example, when building a wrapper component or writing a helper function:

```ts
import type { PropertiesOf } from "elements-kit/custom-elements";
import { XCounter } from "./x-counter";

function applyProps(el: XCounter, props: PropertiesOf<typeof XCounter>) {
  Object.assign(el, props);
}
```

## Typed `document.querySelector` and `createElement`

Independent of any framework, augmenting the global `HTMLElementTagNameMap` gives you typed lookups in plain DOM code:

```ts
declare global {
  interface HTMLElementTagNameMap {
    "x-counter": XCounter;
  }
}

const el = document.querySelector("x-counter"); // XCounter | null
```

Worth adding to the same file as the element class.

***

## See also

* [Custom Elements overview](/custom-elements)
* [React integration](/integrations/react)
* [Solid integration](/integrations/solid)
* [Vue integration](/integrations/vue)
* [Svelte integration](/integrations/svelte)
* [Lit integration](/integrations/lit)
* [Astro integration](/integrations/astro)
* [Qwik integration](/integrations/qwik)
