Summary
Native rendering needs the signals runtime to drive a native view tree instead of the DOM. Today the runtime calls DOM APIs directly, in 268 places. Nothing else about stx-native matters until this is settled: if these calls cannot be factored behind an interface, compiling .stx to native views produces a tree that nothing can update, and the whole approach is dead.
This is the make-or-break spike, and it is answerable in about a week.
Evidence
packages/stx/src/signals.ts is 8,692 lines and reaches for the DOM throughout:
| API | Call sites |
|---|---|
removeAttribute | 63 |
setAttribute | 43 |
document.querySelector | 38 |
addEventListener | 31 |
textContent | 29 |
appendChild | 24 |
createElement | 22 |
classList | 18 |
The binding functions are where it concentrates — bindText is el.textContent = evalAttrExpr(value) (signals.ts:3030), bindShow sets style.display, bindFor builds and moves real nodes.
The encouraging half: signals themselves touch nothing. state, derived, effect and the dependency graph are pure; only the bind* layer and processElement are coupled. The separation this needs already exists conceptually — it just was never given a seam.
Prior art
- React Native — the same split: React's reconciler is renderer-agnostic, and
react-nativesupplies a host config that turns mutations into native view operations. React DOM and React Native share the reconciler outright. - Vue —
@vue/runtime-coretakes acreateRenderer({ createElement, patchProp, insert, remove, setElementText, … });runtime-domand NativeScript-Vue are two implementations of it. - Svelte Native / NativeScript — a DOM shim over native views, which is the cheaper and worse version of the same idea.
Ask
A spike, not a rewrite:
- Define the host interface the
bind*layer needs — roughlycreateNode,setText,setAttribute,removeAttribute,insertBefore,remove,addListener,setStyle,query. - Implement it twice:
dom.ts(exactly today's behaviour, so the web path is unchanged and the existing suite proves it) andnative.ts(emits batched mutations over the bridge protocol inpackages/stx-native/src/bridge/protocol.ts). - Port one binding end to end —
bindText— and nothing else.
Go / no-go: if bindText factors cleanly and the web suite stays green, the rest is grinding, and worth funding. If the interface has to leak DOM specifics to keep the web path working, say so and stop — that answer is worth a week either way.