Lit
Use elements-kit custom elements alongside Lit with typed queries via HTMLElementTagNameMap.
A custom element built with elements-kit is just class extends HTMLElement registered via customElements.define. Lit renders it fine in html template literals — no configuration needed. Because Lit uses tagged templates rather than JSX, there is no IntrinsicElements to augment. The main integration point is getting typed element references in @query decorators and this.renderRoot.querySelector calls.
// 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);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 |
Typed element queries
Augmenting HTMLElementTagNameMap narrows the return type of @query, this.renderRoot.querySelector, and document.querySelector:
// x-counter.ts
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);
declare global {
interface HTMLElementTagNameMap {
"x-counter": XCounter;
}
}Inside a Lit host element:
import { LitElement, html } from "lit";
import { customElement, query } from "lit/decorators.js";
import "./x-counter"; // brings in the HTMLElementTagNameMap augmentation
@customElement("my-host")
export class MyHost extends LitElement {
// @query narrows to XCounter because of HTMLElementTagNameMap
@query("x-counter") counter!: XCounter;
render() {
return html`<x-counter></x-counter>`;
}
updated() {
this.counter.count = 10; // typed
}
}PropertiesOf as a type annotation
PropertiesOf<typeof XCounter> is useful when you need to describe the settable surface of an elements-kit element — for example, inside a factory or test helper:
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);
}