# Lit

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.

```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);
```

## 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`:

```ts
// 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:

```ts
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:

```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);
}
```

***

## See also

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