Qwik
A custom element built with elements-kit is just class extends HTMLElement registered via customElements.define. Qwik renders it fine at runtime, but Qwik’s JSX.IntrinsicElements is independent of elements-kit’s, so you need a small augmentation to get typed props, narrowed ref, and editor autocomplete on the Qwik side.
// 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);Source the prop shape from the class
Two helpers exported from elements-kit/jsx-runtime:
| Helper | Shape | Use when |
|---|---|---|
InstanceProps<I> | Public instance fields only (drops the HTMLElement surface) | You want to type just the user-defined props |
ElementProps<C> | Full elements-kit JSX surface — attrs, fields, events from static events, slots from [SLOTS], children | Your element declares typed events or slots and you want callers to see them |
For most Qwik wiring InstanceProps<XCounter> is enough.
Augment Qwik’s JSX.IntrinsicElements
import type { HTMLAttributes } from "@builder.io/qwik";import type { InstanceProps } from "elements-kit/jsx-runtime";import type { XCounter } from "./x-counter";
declare module "@builder.io/qwik" { namespace JSX { interface IntrinsicElements { "x-counter": HTMLAttributes<XCounter> & InstanceProps<XCounter>; } }}
// <x-counter count={5} ref={counterRef} /> — typedHTMLAttributes<XCounter> provides the standard HTML attribute surface (class, id, event handlers with $ suffix, …) and a ref signal narrowed to XCounter.
Co-locate the augmentation with the class
Put the declare module "@builder.io/qwik" block in the same file as the element class. Importing the class then brings the augmentation along — consumers don’t have to remember to write it themselves.
import { defineElement } from "elements-kit/custom-elements";import { reactive } from "elements-kit/signals";import type { HTMLAttributes } from "@builder.io/qwik";import type { InstanceProps } from "elements-kit/jsx-runtime";
export class XCounter extends HTMLElement { @reactive() count = 0;}
defineElement("x-counter", XCounter);
declare module "@builder.io/qwik" { namespace JSX { interface IntrinsicElements { "x-counter": HTMLAttributes<XCounter> & InstanceProps<XCounter>; } }}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 doing alongside the Qwik augmentation.