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.
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.
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.
Click the card. @State() count increments and
re-renders the template; @Watch('count') emits
the event picked up on the right.
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.
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.
Registers the class as a native custom element with a tag, optional template, and styles.
Reflects a primitive between attribute and property; passes objects and arrays through untouched.
Internal reactive state for render() components. Deeply proxied — mutate nested arrays and objects.
A boolean attribute that follows the W3C spec — present, "true", or "false".
Runs a method whenever a watched property changes — from either side of the bridge.
Wires up DOM events on the host or on any selector inside the shadow tree.
Declares a typed CustomEvent emitter — call .emit() to fire it with a payload.