Skip to content

Marko

A custom element built with elements-kit is just class extends HTMLElement registered via customElements.define. Marko renders it fine at runtime — custom elements with a hyphen in the tag name are passed through as-is. TypeScript support for Marko template types is provided by @marko/language-tools and is evolving; the most reliable typing surface today is HTMLElementTagNameMap for DOM queries.

// 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

Two helpers exported from elements-kit/jsx-runtime:

HelperShapeUse when
InstanceProps<I>Public instance fields only (drops the HTMLElement surface)Annotating setters, wrapper props, or helper functions
ElementProps<C>Full elements-kit JSX surface — attrs, fields, events from static events, slots from [SLOTS], childrenWhen you need the full declared surface

Use the element in a template

Import the element registration before the template renders client-side. In a Marko component, put the import in <script> or the component’s class body:

x-counter.marko
import "./x-counter"; // registers the custom element
<x-counter count=5 />

Typed document.querySelector and createElement

Augmenting the global HTMLElementTagNameMap gives you typed DOM lookups in any TypeScript file — Marko template logic, server routes, or client scripts:

types/elements.d.ts
import type { XCounter } from "./src/x-counter";
declare global {
interface HTMLElementTagNameMap {
"x-counter": XCounter;
}
}

Make sure this file is covered by your tsconfig.json’s include array.

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

InstanceProps<XCounter> is useful as a type annotation when writing helper functions that operate on the element:

import type { InstanceProps } from "elements-kit/jsx-runtime";
import { XCounter } from "./x-counter";
function applyProps(el: XCounter, props: InstanceProps<XCounter>) {
Object.assign(el, props);
}

See also