svelte
svelte copied to clipboard
<svelte:element this="svg"> generates proper html but doesn't get rendered properly.
Describe the bug
I am using a python library that builds a json describing the html components. This is then fed to a svelte code that uses svelte:element to instantiate the html components. All components (button/iinput) works fine except for svg elements. The html is generated properly as shown below
<button value="9" class="...." style=""> <svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" style=""> </path></svg></button>```
however the svg doesn't gets rendered on the screen (the button does though)
### Reproduction
Kind of tricky for this case to build a reproduction. If really needed, I can try. Let know.
### Logs
_No response_
### System Info
```shell
System:
OS: Linux 5.15 Debian GNU/Linux 11 (bullseye) 11 (bullseye)
CPU: (4) arm64 Cortex-A72
Memory: 2.39 GB / 3.68 GB
Container: Yes
Shell: 5.1.4 - /bin/bash
Binaries:
Node: 18.3.0 - /usr/bin/node
npm: 8.11.0 - /usr/bin/npm
Browsers:
Firefox: 91.10.0esr
npmPackages:
svelte: ^3.48.0 => 3.48.0
Severity
annoyance
I believe you have to define the width or height of the SVG using css / inline style. e.g:
button svg {
height: 16px;
}
This is default behaviour. Your usage of svelte:element is fine and not causing issues.
This didn't help. I have two svg elements : one in plain html, another using svelte. They both are identcal in definition. HTML renders fine. Svelte one doesn't. Attaching the html and the output:
<div id="components">
<div class="container mx-auto flex justify-center">
<button value="9" class="bg-pink-100 text-gray-600 mr-1 mb-1 px-4 py-2 font-bold outline-none shadow shadow-sm rounded-md font-bold uppercase ease-linear transition-all duration-150 outline-none focus:outline-none hover:shadow-md hover:bg-gray-200 mx-1" style=""> <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" style=""> </path></svg></button>
</div>
<div class="mx-auto container h-screen bg-gray-100/20" style=""> <div class="flex justify-center" style=""> <button value="9" class="bg-gray-100 text-gray-600 mr-1 mb-1 px-4 py-2 font-bold outline-none shadow shadow-sm rounded-md font-bold uppercase ease-linear transition-all duration-150 outline-none focus:outline-none hover:shadow-md hover:bg-gray-200 mx-1" style=""> <svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" width="16px" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" style=""> </path></svg></button></div></div></div>

@sandeep-gh In the code that you have shared, the first svg element does not contain a height or width values. The second one does contain width, that is the reason why the second one is rendering and the first one isn't. As Yimmie suggested above, you need to add a height or width to it.
As @sami-baadarani said, the first svg element does not contain a height or width values. After adding width or height, they both work well. @sandeep-gh

I came across something I believe is similar to this issue.
I'm doing something like this:
App.svelte
<script>
import DynamicTag from "./DynamicTag.svelte";
</script>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:map="http:rsvg.org/dtd/map" viewBox="0 0 1024 1024">
<DynamicTag element={"g"}><line stroke-width="3" stroke="#000" fill="#000" x1="100" x2="400" y1="100" y2="400" /></DynamicTag>
</svg>
DynamicTag.svelte
<script>
export let element = "div";
</script>
<svelte:element this={element}><slot /></svelte:element>
The html generated and retrieved through the browser developer tools is flawless and will work if used statically, but renders nothing within my svelte app. The DynamicTag component will work for any non-svg tag, or if svelte:element isn't used (but that'd defeat the purpose obviously).
TLDR Not really having looked into this much, my guess would be that Svelte underneath creates the element using document.createElement and g, along with all other svg tags, only work when created with document.createElementNS.
I got curious and looked into the hint that @nicksulkers left. It looks like there are two relevant functions in src/runtime/internal/dom.ts:
element-> calls document.createElementsvg_elementcalls document.createElementNS with the SVG namespace
If you use a static name, the compiler is able to figure out that you want to use svg_element & imports it. Or you can add <svelte:options namespace="svg" /> to force the import.
The generated code for DynamicTag.svelte imports element & uses it:
import {
// ...
element as element_1,
// ...
} from "svelte/internal";
function create_dynamic_element(ctx) {
// ...
return {
c() {
svelte_element = element_1(/*element*/ ctx[0]);
I was thinking it could be hard for the compiler to figure out that an arbitrary name is supposed to use the svg namespace, but that maybe adding the namespace would help. When you do, the correct function is brought in, but the compiler takes a different path & in the output, the element name ends up being svelte:element (which you can even see in the inspector)
import {
//...
svg_element,
} from "svelte/internal";
function create_dynamic_element(ctx) {
// ...
return {
c() {
svelte_element = svg_element("svelte:element");
it seems like we'd want it to output this:
import {
// ...
svg_element,
// ...
} from "svelte/internal";
function create_dynamic_element(ctx) {
// ...
return {
c() {
svelte_element = svg_element(/*svg_element*/ ctx[0]);
(but I don't know how to do that 🤗)
postscript: I don't think it's simply about the presence or absence of a height or width attribute, because adding either to the sag element in App.svelte doesn't fix the problem
Seeing the same problem here using svelte:element and svelte:self to generate an SVG structure. Once I figured out it was a namespace problem, I was able to add <svelte:options namespace="svg /> and get it working, but I think the intent (based on some older issues I looked up) is that Svelte should be auto-detecting and applying the namespace as needed for these?
This should be supported now as of 3.51.0.
Closed by #7695.
I am not able to get this to work. Below is setup:
package.json
{
...
...
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.11",
...
"tailwindcss": "^3.1.8",
"vite": "^2.3.7"
},
"dependencies": {
"svelte": "^3.51.0"
}
}
The html begin generate:
<div class="flex justify-center" style=""> <div class="flex flex-col space-y-4" style="">
<button value="myval" class="" style="">Click me
<svg viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" class="h-6 w-6" style=""> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" style=""> </path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" style=""> </path></svg>
</button>
</div>
</div>
However, no svg is getting rendered. Not sure if I am missing something.
I can see the SVG. https://svelte.dev/repl/c63e6c14acfa4b06a5af56b350261330?version=3.51.0
Maybe I can easily understand the situation if you create REPL.
Here is a repl, which shows that the problem persists in v.3.55.0. https://svelte.dev/repl/5e1e989bbccb4d688b571006bc3c0d12?version=3.55.0
I would expect to see both the red and the green circle, since both look identical in html, but the green circle isn't rendered. Refreshing the parent element of the svg makes it show up. (https://stackoverflow.com/questions/36339444)
What is the use case of this? If we support this, we need to add an additional runtime check and it has a performance overhead.
I have an one use case for this. I am developing a web development framework in Python based on justpy (https://github.com/justpy-org/justpy/). I am using svelte as the frontend library. All the components of the webpage is described as a json and shipped over websocket to frontend where it is rendered using svelte. The use case for rendering svg comes in this regard.
Why just using {#if tag === 'svg'} is not enough?
I will try the tag=== 'svg' and get back.
Let me summarize: the problem is that svg elements need to be created with a different namespace. The namespace can't be changed afterwards.
In general, the problem can occur on all svg tags (svg, path, circle, etc ). I say 'can', since svelte is smart enough to change the namespace, if the svelte:element is a visual child of an svg tag. Example:
<svg>
<svelte:element this={toggle?"circle":"ellipse"} {...props}/>
</svg>
This works. If you extract just the svelte:element into a different component, it would fail.
<svelte:options namespace='svg'/> can be used to work around this problem.
We have easy workaround that is using <svelte:options namespace='svg'/>.
So I close this issue for now. If still someone has a issue, we can reopen this.
@baseballyama workaround works for me now. Thank you.