Derive JSX prop types from your components — attributes, events, slots, children — instead of maintaining a parallel declare global block. One helper for most cases, specialized helpers when you need more.
All type helpers are re-exported from elements-kit/jsx-runtime. MaybeReactive also lives in elements-kit/signals.
ElementProps<C>
Raw JSX surface for a custom element. Composes attributes, flat properties, prop:*, events, slots, and children — withoutMaybeReactive wrapping. Use this when you need the underlying property/handler types directly (testing, manual property assignment, deriving sub-types).
A decorator that makes a class field reactive by automatically wrapping its value in a signal.
The field behaves like a normal property (get/set) but reactivity is tracked under the hood.
Any reads will subscribe to the signal and any writes will trigger updates.
@example
classCounter {
\@reactive() count:number=0;
}
constcounter=newCounter();
counter.count++; // Triggers reactivity
console.log(counter.count); // Subscribes to changes
@remarks ―
Equivalent to manually creating a private signal and getter/setter:
classCounter {
#count=signal(0);
getcount() { returnthis.#count(); }
setcount(value) { this.#count(value); }
}
reactive } from"elements-kit/signals";
import {
constSLOTS:typeofSLOTS
Symbol key for attaching a slot collection to a custom element instance.
Prevents collisions with public Element properties and signals to the JSX
runtime that this property holds slot wiring (not regular children).
The value at [SLOTS] is a plain object whose keys are slot names and whose
values are
Slot
instances. Declare with as const so TypeScript
preserves the literal key union — this is what ElementProps<typeof Cls>
uses to synthesize slot:${K} entries.
@example
classCardextendsHTMLElement {
// ✅ literal keys flow through — "header" | "footer"
// [SLOTS] = { header: new Slot(), footer: new Slot() };
SLOTS,
classSlot
A lightweight slot that reserves a region in the DOM using comment markers.
Content between the markers can be replaced dynamically without wrapper elements.
Full JSX prop type for a custom-element class (extends HTMLElement).
Composes every surface the element can receive from JSX:
Attributes — keys from static [ATTRIBUTES] (typed MaybeReactive<string | null>).
Keys also present on the instance are dropped here so the flat key carries the property type.
Flat properties — public instance fields, wrapped in MaybeReactive.
prop:* — explicit property assignment for every field.
Events — keys from declare static events: { ... } produce both
on:${K} and on${Capitalize<K>} typed handlers.
Slots — keys from [SLOTS] = { ... } as const produce slot:${K}.
Children — children?: Child unless static children: never.
DOM attrs — the standard dom-expressions surface (class, style, ref, …).
@template ― C — the custom-element class (constructor type).
A class decorator that automatically wires up observedAttributes and attributeChangedCallback
from a static [ATTRIBUTES] map.
The this type inside attribute handlers is automatically inferred from the decorated class.
@example
\@attributes
classMyElementextendsHTMLElement {
static [ATTRIBUTES] = {
count(this:MyElement, value:string|null) {
this.count =Number(value);
},
};
}
attributes
class
classXRange
XRangeextends
var HTMLElement: {
new ():HTMLElement;
prototype:HTMLElement;
}
The HTMLElement interface represents any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it.
Symbol key for attaching a slot collection to a custom element instance.
Prevents collisions with public Element properties and signals to the JSX
runtime that this property holds slot wiring (not regular children).
The value at [SLOTS] is a plain object whose keys are slot names and whose
values are
Slot
instances. Declare with as const so TypeScript
preserves the literal key union — this is what ElementProps<typeof Cls>
uses to synthesize slot:${K} entries.
@example
classCardextendsHTMLElement {
// ✅ literal keys flow through — "header" | "footer"
// [SLOTS] = { header: new Slot(), footer: new Slot() };
SLOTS] = {
label: Slot
label: new
newSlot(): Slot
A lightweight slot that reserves a region in the DOM using comment markers.
Content between the markers can be replaced dynamically without wrapper elements.
A decorator that makes a class field reactive by automatically wrapping its value in a signal.
The field behaves like a normal property (get/set) but reactivity is tracked under the hood.
Any reads will subscribe to the signal and any writes will trigger updates.
@example
classCounter {
\@reactive() count:number=0;
}
constcounter=newCounter();
counter.count++; // Triggers reactivity
console.log(counter.count); // Subscribes to changes
@remarks ―
Equivalent to manually creating a private signal and getter/setter:
Full JSX prop type for a custom-element class (extends HTMLElement).
Composes every surface the element can receive from JSX:
Attributes — keys from static [ATTRIBUTES] (typed MaybeReactive<string | null>).
Keys also present on the instance are dropped here so the flat key carries the property type.
Flat properties — public instance fields, wrapped in MaybeReactive.
prop:* — explicit property assignment for every field.
Events — keys from declare static events: { ... } produce both
on:${K} and on${Capitalize<K>} typed handlers.
Slots — keys from [SLOTS] = { ... } as const produce slot:${K}.
Children — children?: Child unless static children: never.
DOM attrs — the standard dom-expressions surface (class, style, ref, …).
@template ― C — the custom-element class (constructor type).
Attribute keys that also appear on the instance are removed from the attribute slot, so the flat key carries the property type (e.g. number) rather than the handler’s string type.
At the JSX call site, the runtime wraps ElementProps<C> in MaybeReactiveProps<...> so parents may pass static values or signals/computed:
// JSX layer wraps via `MaybeReactiveProps<ElementProps<C>>`
<x-rangemin={0} /> // static
<x-rangemin={() =>0} /> // getter
<x-rangemin={signal(0)} /> // signal (a getter)
Plus the namespaced extras (class:, style:, prop:, slot:, ref) are layered on every intrinsic element via JsxNamespaces in src/jsx-runtime/index.ts.
Props<C>
Unified helper. Works on class instances, class constructors, or function components. Does not synthesize attribute / event / slot surfaces — use ElementProps for those.
so callers may pass
either a plain value or a reactive getter. Function-typed props (event
handlers, render callbacks) are left as-is so inline arrow callbacks get
proper contextual typing for their parameters — wrapping them in
MaybeReactive<F> = F | (() => F) produces two callable alternatives and
TS falls back to implicit-any on the callback's params. Optionality is
preserved at the key level — the | undefined stays at the prop, not
inside the reactive.
so callers may pass
either a plain value or a reactive getter. Function-typed props (event
handlers, render callbacks) are left as-is so inline arrow callbacks get
proper contextual typing for their parameters — wrapping them in
MaybeReactive<F> = F | (() => F) produces two callable alternatives and
TS falls back to implicit-any on the callback's params. Optionality is
preserved at the key level — the | undefined stays at the prop, not
inside the reactive.
Component-facing — every prop becomes a callable Computed<T> getter. The JSX runtime auto-wraps function-component props into this shape, so you read props.count() and the read subscribes.
ReactiveProps<P> carries a phantom RAW_PROPS brand so the JSX layer (ResolveProps) can recover the original P and translate the call-site type back to MaybeReactiveProps<P> for parents.
RawProps<R>
Recovers the raw prop shape P from a branded ReactiveProps<P>. For non-branded inputs returns the input unchanged. Mostly useful internally inside ResolveProps, but exported for advanced typing.
The JSX call-site translator wired into JSX.LibraryManagedAttributes. Decides what attributes the parent is allowed to write for any component (function or class):
You rarely need to use ResolveProps directly — it runs implicitly for every component JSX expression. Custom-element JSX types come from ElementProps<C> instead (different code path).
MaybeReactive<T>
A scalar value or a zero-arg getter that returns the value. Usually a signal or computed.
The getter is only called when the computed value is read and one of
its dependencies has changed since the last evaluation. If nothing has
changed the cached value is returned without re-running getter.
Computed values are read-only; they cannot be set directly.
@param ― getter - Pure function deriving a value from other reactive sources.
Receives the previous value as an optional optimisation hint.
@example
consta=signal(1);
constb=signal(2);
constsum=computed(() =>a() +b());
sum(); // → 3
a(10);
sum(); // → 12 (re-evaluated lazily)
computed } from"elements-kit/signals";
importtype {
typeMaybeReactive<T> =T|Computed<T>
A value that may be static or reactive. Accepts a plain T or a
zero-arg getter (() => T) — typically a signal or computed.
Used across the library anywhere a prop or attribute may be bound to
reactive state. Resolve with
Read: call with no arguments → returns the current value and
subscribes the active tracking context.
Write: call with a value → updates the signal and schedules
downstream effects if the value changed.
@example
constcount=signal(0);
count(); // → 0 (read)
count(1); // write – effects depending on count will re-run
count(); // → 1
signal(0);
const
constdouble: () =>number
double=
computed<number>(getter: (previousValue?:number|undefined) => number): () => number
Creates a lazily-evaluated computed value.
The getter is only called when the computed value is read and one of
its dependencies has changed since the last evaluation. If nothing has
changed the cached value is returned without re-running getter.
Computed values are read-only; they cannot be set directly.
@param ― getter - Pure function deriving a value from other reactive sources.
Receives the previous value as an optional optimisation hint.
@example
consta=signal(1);
constb=signal(2);
constsum=computed(() =>a() +b());
sum(); // → 3
a(10);
sum(); // → 12 (re-evaluated lazily)
computed(() =>
constcount: () =>number (+1overload)
count() *2);
const
consta:MaybeReactive<number>
a:
typeMaybeReactive<T> =T|Computed<T>
A value that may be static or reactive. Accepts a plain T or a
zero-arg getter (() => T) — typically a signal or computed.
Used across the library anywhere a prop or attribute may be bound to
reactive state. Resolve with