v0.0.16 TypeScript 5 · MIT · 0 deps

Native Web Components, written in TypeScript.

custom-elements-ts is a tiny set of decorators — @CustomElement, @Prop, @State, @Watch, @Listen, @Dispatch, @Toggle — plus a 3 kB html/render() runtime that lets you author standards-compliant custom elements without a framework.

  Works with any framework — or none   ~3 kB min+gzip runtime   Shadow DOM & light DOM
message.element.ts
Live in your browser

Two demos. Real components. Real events.

Each demo below is a custom element built with the same library you'd npm install. The event log on the side is itself a render() + @State() component — it subscribes to the bubbling, composed CustomEvents the demo dispatches.

Demo · 01

Counter — @State() + @Dispatch()

A single-button card that owns an internal count, re-renders via the html tagged template, and fires counter.change whenever its @State() changes.

GitHub

Click the card. @State() count increments and re-renders the template; @Watch('count') emits the event picked up on the right.

Demo · 02

Sprint board — composition, deep state, custom events

Four cooperating elements: a parent dashboard that owns the source of truth, plus child stats, filters, and item rows that talk back through bubbling, composed events. Mutate a nested array — the parent re-renders.

GitHub
The API

Seven decorators. That's the whole library.

Every decorator maps directly onto a built-in browser primitive — attributes, properties, events, MutationObserver. No magic, no virtual DOM, no compiler required.

@CustomElementclass

Registers the class as a native custom element with a tag, optional template, and styles.

@Propproperty

Reflects a primitive between attribute and property; passes objects and arrays through untouched.

@Stateproperty

Internal reactive state for render() components. Deeply proxied — mutate nested arrays and objects.

@Toggleproperty

A boolean attribute that follows the W3C spec — present, "true", or "false".

@Watchmethod

Runs a method whenever a watched property changes — from either side of the bridge.

@Listenmethod

Wires up DOM events on the host or on any selector inside the shadow tree.

@Dispatchproperty

Declares a typed CustomEvent emitter — call .emit() to fire it with a payload.