JoeTidee
JoeTidee

Reputation: 26054

React - Using svg <defs> with dangerouslySetInnerHTML

I am trying to use svg with <defs> defined elsewhere in the page.

const data =  {
  icons: {
    freezable: "<svg><path d=""/></svg>",
     vegetarian: "<svg><path d=""/></svg>",
  }
};

const SvgDefs = (icons) => {
  return Object.keys(icons).map(iconName => {
    return <span key={data.icons[iconName]} dangerouslySetInnerHTML={{
      __html: data.icons[iconName].replace('svg', `svg id="svg-${iconName}"`)
    }} />;
  });
};

With my component rendering this as:

 return <svg aria-hidden="true" id="svgdefs" xmlns="http://www.w3.org/2000/svg">
   <defs>
      <SvgDefs icons={data.icons} />
    </defs>
 </svg>

However, when I check the Elements tab in Chrome Dev Tools, I see that the spans containing the svgs are outside of the <defs> block:

enter image description here

What am I doing wrong here?

Upvotes: 0

Views: 977

Answers (2)

The problem comes from you calling your functional component's input argument icons, and the assumptions that stem from that naming choice.

Functional components are passed JSX properties wrapped up in an object, conventionally called props, so call your function input props and then get your data back out from that. Since you passed icons=..., you'll need access that data through props.icons:

const SvgDefs = (props) => {
  const { icons } = props;
  return Object.entries(icons).map(([name, html]) => {
    return <span key={name} dangerouslySetInnerHTML={{
      __html: html.replace('svg', `svg id="svg-${name}"`)
    }} />;
  });
};

Although it would make a lot more sense to not have SVG strings in your data.icons to begin with: just have more JSX so you don't need to dangerously set innerHTML at all.

Upvotes: 1

JoeTidee
JoeTidee

Reputation: 26054

I discovered that the reason was because <span> cannot be used inside <defs>. I used <symbol> instead and all worked as expected.

Upvotes: 0

Related Questions