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.
// shared element β built once, used anywhereimport { 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:
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:
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:
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:
declare global { interface HTMLElementTagNameMap { "x-counter": XCounter; }}
const el = document.querySelector("x-counter"); // XCounter | nullWorth adding to the same file as the element class.