13

Question: Can I render an SVGSVGElement in React without using dangerouslySetInnerHtml?

Context:

I am using the vis.js graph library, the getLegend method returns an SVGSVGElement object i.e. const icon = chart.getLegend(args); In the console I can see this:

in: icon instanceof SVGSVGElement
out: true
in: icon
out: <svg><rect x="0" y="0" width="30" height="30" class="vis-outline"></rect><path class="vis-graph-group0" d="M0,15 L30,15"></path></svg>

Problem:

When I try to render this in react using:

render (
<div> { icon } </div>
)

I get the following error:

Error: Objects are not valid as a React child (found: [object SVGSVGElement]). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `LegendElement`

Workaround:

For now I am using: <svg dangerouslySetInnerHTML={{__html: icon.innerHTML}} />

But I was hoping for a straightforward solution that doesn't use a method with the word dangerous in the name.

Research:

I read this similar question but I don't think it helps for SVG generated at run time: How do I use an SVG in React without using dangerouslySetInnerHTML?

YakovL
  • 7,557
  • 12
  • 62
  • 102
maikthomas
  • 427
  • 5
  • 15

1 Answers1

12

You can simple append the SVGSVGElement using a useRef like this. This example is for a functional component with hooks but can be adapted for class components.

const svg = useRef(null);
useEffect(()=>{
    if(svg.current){
        svg.current.appendChild(icon)
    } 
}, []);

return (
    <div ref={svg}/>
);
Ihsan Müjdeci
  • 914
  • 1
  • 9
  • 14
  • Thanks Ihsan,If anyone can verify this and let me know to mark the answer. I no longer have access to the project I needed this for and don't have time to set up a PoC :D – maikthomas Apr 21 '20 at 14:51
  • This sort of works, but the SVG is like squished until I click on it. But that is probably due to something else (I'm trying to get a directed force graph from D3 to render in there) – Asu Dec 07 '20 at 18:38
  • I finally got the chance to validate this answer and it works perfectly at least in my case. Thanks Ishan! – maikthomas Jun 02 '21 at 10:04
  • @Asu; did you ever get this to work for your force graph? I have the same problem. – Xari Jan 30 '22 at 15:31
  • @Xari Yes, I had to add this just before render's return statement: `if (d3Chart.simulation && d3Chart.simulation.restart) {d3Chart.simulation.restart();}` – Asu Jan 30 '22 at 19:21
  • 1
    It rendered two `svg` icons. So, I changed the `if` condition like this. `if (svg.current && (svg.current as any).innerHTML === "")` – Sanka Sanjeewa Mar 01 '23 at 14:51