mikemklee
mikemklee

Reputation: 483

React SVG disappearing when component rerenders

I am making a react application with some SVG icons within my components. For instance, I have a SearchBar component which includes the input element along with a button that has a Search Icon in it.

I am using inline SVG for all my SVG icons, by setting the xlinkHref attribute accordingly from a single sprite file.

Everything seems to work just fine, except that my SVG icons randomly disappear when I click on them. The button element that contains the icon does not disappear, just the SVG icon. I checked the DOM tree on the devtools, and it seems like that there is an issue with a node called "#shadow-root".

When my SVG icons are visible, they are placed as child nodes of the "#shadow-root". However, when they disappear, the DOM tree shows that "#shadow-root" node no longer has any child node, effectively making my SVG icons nowhere to be found.

Before disappearing: enter image description here

After disappearing: enter image description here

It is heart-breaking to see my lovely SVG icons suddenly disappear...

Please advise!

-----EDIT----- Here is the code for the component that I am using to insert SVG icons:

import React from "react";

const Icon = ({ name }) => (
  <svg>
    <use xlinkHref={`img/icons/sprite.svg#icon-${name}`} />
  </svg>
);
export default Icon;

Upvotes: 15

Views: 5306

Answers (2)

Lucretiel
Lucretiel

Reputation: 3334

You can build the SVG element yourself. You can let React build the empty <svg> element, then manually insert a <use> element using a ref and an effect:

const Icon = React.memo(({ url }) => {
  const [svgNode, setSvgNode] = React.useState(null);

  React.useLayoutEffect(() => {
    if (svgNode === null) {
      return;
    }

    const useNode = document.createElementNS("http://www.w3.org/2000/svg", "use");
    useNode.setAttributeNS("http://www.w3.org/1999/xlink", "href", url);
    svgNode.appendChild(useNode);

    return () => {
      useNode.remove();
    };
  }, [type, svgNode]);

  return <svg ref={setSvgNode} />;
});

Based on this implementation.

Upvotes: 0

Nico Diz
Nico Diz

Reputation: 1504

you can try another ways to include your SVG. I mentione three possibilities:

  1. Use the .svg file as an img src, as we can see in create-react-app:
    import logo from './logo.svg';

    render() {...
          <img src={logo} className="App-logo" alt="logo" />
          ...}
  1. Use svg-inline-react. For more info check here :
    import InlineSVG from 'svg-inline-react';

    const svgSource = `<svg xmlns="......</svg>`;
    <InlineSVG src={svgSource} />
  1. Convert the SVG into base64 and set it in the background image of your button.

Good luck.

Upvotes: 2

Related Questions