Quick start
Start with signals, grow into a component, end as a native HTML tag. Each step below adds one layer on top of the previous β the reactive core never changes, only how you package it.
From signal to custom element
Whatβs happening
- Step 1 β signals.
signal(0)holds state;computed(() => count() * 2)derives from it. Both are framework-agnostic β no DOM involved. - Step 2 β signals + JSX.
render(target, setup)runssetupinside aneffectScope, appends the returned node totarget, and hands back a singleunmountthunk.{count}creates a live text node that updates in place β no diffing, no re-render. - Step 3 β function component. Props are typed with
ReactiveProps<T>; each key onpropsarrives as aComputed<T>(callable getter). Return type isElementβ a real DOM node. - Step 4 β class component.
@reactive()turns class fields into signals with natural property access.render(): Elementreturns the same shape, called once when the instance mounts. - Step 5 β custom element.
@attributeswires thestatic [attr]map so HTML attributes feed reactive properties.connectedCallbackmounts;disconnectedCallbacktears down. Usable as a plain HTML tag in any framework or no framework at all.
The reactive core (steps 1β2) is doing all the work. Steps 3β5 are just packaging.
Next steps
- Learn the signal primitives in depth β Signals.
- Understand JSX β DOM compilation β Elements.
- Wrap the same pattern in a native custom element β Custom elements.