Repository navigation
Description
Describe the bug
When <Dynamic> renders an SVG element whose tag name also exists in HTML, the element is created in the HTML namespace during client-side rendering.
For example, a static <a> inside <svg> is correctly created in the SVG namespace, while <Dynamic component="a"> in the same parent becomes an HTML element.
This creates a CSR/SSR discrepancy:
- CSR creates an HTML
<a>. - Server-rendered markup is parsed by the browser as an SVG
<a>. - Hydration can therefore begin with the correct namespace, while client-only rendering or a later dynamic replacement produces a different DOM structure.
Steps to Reproduce the Bug or Issue
- Render an containing both a static and .
- Read namespaceURI from both elements.
- Observe that the static element uses the SVG namespace while the dynamic element uses the HTML namespace.
The following regression test demonstrates the difference:
test("creates ambiguous SVG tags in the SVG namespace", () => {
let svg!: SVGSVGElement;
createRoot(() => (
<svg ref={svg}>
<a id="static" href="#target" />
<Dynamic component="a" id="dynamic" href="#target" />
</svg>
));
const staticAnchor = svg.querySelector("#static")!;
const dynamicAnchor = svg.querySelector("#dynamic")!;
expect(staticAnchor.namespaceURI).toBe("http://www.w3.org/2000/svg");
expect(dynamicAnchor.namespaceURI).toBe("http://www.w3.org/2000/svg");
});The final assertion currently fails with:
Expected: "http://www.w3.org/2000/svg"
Received: "http://www.w3.org/1999/xhtml"
Expected behavior
<Dynamic component="a"> inside an <svg> should create an element in the SVG namespace, matching:
<svg>
<a />
</svg>Client rendering and server-rendered markup should produce elements with the same namespace.
Platform
- Solid: next at f051db6
- The same tag-set-based creation path is present in Solid 1.9.15
- Test environment: Vitest with jsdom
- OS: Linux
Additional context
The client runtime determines the namespace only from the tag name:
SVGElements.has(tagName)
? document.createElementNS(Namespaces.svg, tagName)
: MathMLElements.has(tagName)
? document.createElementNS(Namespaces.mathml, tagName)
: document.createElement(tagName, { is });Ambiguous SVG tag names such as a, title, script, and style are not included in SVGElements, because the same names also exist in HTML. As a result, always falls back to document.createElement("a"), regardless of its SVG parent.
A possible fix may require propagating the parent namespace to dynamically created intrinsic elements or providing an explicit namespace hint for .
The closest related work I found was ryansolid/dom-expressions#494, which addressed namespace handling for static XML partials but does not appear to cover dynamic intrinsic elements.
Activity
fix(web): create ambiguous SVG tags in the SVG namespace in Dynamic c…
ryansolid commentedon Sep 1, 2026
Fixed in 3c357fb on next — Dynamic now defers intrinsic element creation until insertion, so ambiguous tags (a/script/style/title) resolve against the actual insertion parent's namespace and SVG elements are created in the SVG namespace. Ships in the next RC.
revert(web): restore eager element creation in Dynamic (#3187 deferral;
ryansolid commentedon Sep 6, 2026
refactor(web): unified For hole demote is synchronous in all modes; r…
refactor(web): unified For hole demote is synchronous in all modes; r…
refactor(web): unified For hole demote is synchronous in all modes; r…
refactor(web): unified For hole demote is synchronous in all modes; r…