Version: 2.0.0-rc.8 (@solidjs/web)
Two correctness gaps in the string-tag branch of dynamic() (packages/web/src/index.ts ~366–374), which Dynamic uses:
case "string":
const el = sharedConfig.hydrating
? getNextElement()
: createElement(component as string, untrack(() => (props as any).is));
spread(el, props);
return el;
1. Hydration event replay never runs
Compiled JSX emits _$runHydrationEvents() after any element with event handlers (babel-plugin/src/dom/element.ts ~323). dynamic() binds handlers through spread(el, props) and never calls it. Events queued by the hydration header script for a <Dynamic component="button" onClick=…> are replayed only if some other compiled element on the page happens to call runHydrationEvents() after it hydrates; if the interactive elements are all Dynamic, queued clicks are dropped until _$HY.done. @yak/solid had to add the call in its own element renderer.
Fix: call runHydrationEvents() after spread when hydrating (cheap — it's a no-op when nothing is queued).
2. Namespace is chosen from the tag alone
createElement(tag) picks the namespace via SVGElements.has(tag) / MathMLElements.has(tag). Tags that exist in both HTML and SVG — a, script, style, title — are always created as HTML, so <svg><Dynamic component="a"> produces an HTML anchor inside an SVG tree. The compiler resolves this from the parent at build time; the runtime path has no parent context. This is why @yak/solid carries an ambiguousSvgTags set and routes those four tags differently.
Fix options: (a) accept the namespace from the mount context — insert/the parent element is known at spread time (el.parentNode after insertion is too late; a context or an explicit ns option would work); (b) at minimum document the limitation and expose the set so libraries don't hard-code it.
Related
Version: 2.0.0-rc.8 (
@solidjs/web)Two correctness gaps in the string-tag branch of
dynamic()(packages/web/src/index.ts~366–374), whichDynamicuses:1. Hydration event replay never runs
Compiled JSX emits
_$runHydrationEvents()after any element with event handlers (babel-plugin/src/dom/element.ts~323).dynamic()binds handlers throughspread(el, props)and never calls it. Events queued by the hydration header script for a<Dynamic component="button" onClick=…>are replayed only if some other compiled element on the page happens to callrunHydrationEvents()after it hydrates; if the interactive elements are allDynamic, queued clicks are dropped until_$HY.done.@yak/solidhad to add the call in its own element renderer.Fix: call
runHydrationEvents()afterspreadwhen hydrating (cheap — it's a no-op when nothing is queued).2. Namespace is chosen from the tag alone
createElement(tag)picks the namespace viaSVGElements.has(tag)/MathMLElements.has(tag). Tags that exist in both HTML and SVG —a,script,style,title— are always created as HTML, so<svg><Dynamic component="a">produces an HTML anchor inside an SVG tree. The compiler resolves this from the parent at build time; the runtime path has no parent context. This is why@yak/solidcarries anambiguousSvgTagsset and routes those four tags differently.Fix options: (a) accept the namespace from the mount context —
insert/the parent element is known at spread time (el.parentNodeafter insertion is too late; a context or an explicitnsoption would work); (b) at minimum document the limitation and expose the set so libraries don't hard-code it.Related
dynamic()still allocates a factory memo and a per-instance memo to track a tag that can't change — tracked in Constant-tagDynamic/dynamic()should take the compiled-element path #3387 and the perf tracker Perf tracker: Solid primitives vs the hand-rolled@yak/solidruntime #3389.@yak/solidaudit (DigitecGalaxus/next-yak#644).