Turning an SVG string into an image in a React component

15,475

Solution 1

Since the SVG is dynamically generated and you can't store it as an asset, as an alternative to dangerouslySetInnerHTML, you could simply set it as a Data URI on the image. So something like...


class SomeComponent extends React.Component {
    render() {
        const image = '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" width="47.4" height="40.65" viewBox="21 18.5 158 135.5"><path d="M25,50 l150,0 0,100 -150,0 z" stroke-width="4" stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path><path d="M25,50 L175,150 M25,150 L175,50" stroke-width="4" stroke="black" fill="black" ></path><g transform="translate(0,0)" stroke-width="4" stroke="black" fill="none" ><circle cx="100" cy="30" r="7.5" fill="black" ></circle><circle cx="70" cy="30" r="7.5" fill="black" ></circle><circle cx="130" cy="30" r="7.5" fill="black" ></circle></g></svg>';
        return (
            <div>
              <img src={`data:image/svg+xml;utf8,${image}`} />
            </div>
        )
    }
}

See post here: https://css-tricks.com/lodge/svg/09-svg-data-uris/

Solution 2

One thing you can do is to convert your svg string to base64 and then use it like this:

const image = '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" width="47.4" height="40.65" viewBox="21 18.5 158 135.5"><path d="M25,50 l150,0 0,100 -150,0 z" stroke-width="4" stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path><path d="M25,50 L175,150 M25,150 L175,50" stroke-width="4" stroke="black" fill="black" ></path><g transform="translate(0,0)" stroke-width="4" stroke="black" fill="none" ><circle cx="100" cy="30" r="7.5" fill="black" ></circle><circle cx="70" cy="30" r="7.5" fill="black" ></circle><circle cx="130" cy="30" r="7.5" fill="black" ></circle></g></svg>';
const buff = new Buffer(image);
const base64data = buff.toString('base64');

return <img src='data:image/svg+xml;base64,${base64data }' alt="" />

if you don't want to use buffer, use this:

const base64data = btoa(unescape(encodeURIComponent(image)));

Solution 3

React ref with innerHTML works quite well and is clean.

var image = '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" width="47.4" height="40.65" viewBox="21 18.5 158 135.5"><path d="M25,50 l150,0 0,100 -150,0 z" stroke-width="4" stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path><path d="M25,50 L175,150 M25,150 L175,50" stroke-width="4" stroke="black" fill="black" ></path><g transform="translate(0,0)" stroke-width="4" stroke="black" fill="none" ><circle cx="100" cy="30" r="7.5" fill="black" ></circle><circle cx="70" cy="30" r="7.5" fill="black" ></circle><circle cx="130" cy="30" r="7.5" fill="black" ></circle></g></svg>';


const useApp = () => {
  const svgWrapperRef = React.useRef();
  React.useEffect(() => {
    svgWrapperRef.current.innerHTML = image;
  }, [])
  return {
    svgWrapperRef
  }
}
const App = () => {
  const {svgWrapperRef} = useApp()
  return (
    <div ref={svgWrapperRef}></div>
  )
}

const root = document.getElementById('root')

ReactDOM.render(<App />, root)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

<div id="root"></div>

Good Luck...

Solution 4

Simply use this package: https://github.com/gilbarbara/react-inlinesvg

Example:

import SVG from 'react-inlinesvg';

...    

const mySVG = '<svg xmlns="http://www.w3.org/2000/svg">...</svg>';
return <SVG src={mySVG} />;
Share:
15,475

Related videos on Youtube

James Paterson
Author by

James Paterson

I'm a developer in the UK, currently working for Tripadvisor Experiences. Visit my website here - thejamespaterson.com

Updated on June 06, 2022

Comments

  • James Paterson
    James Paterson almost 2 years

    I have a dynamically generated SVG string in a React component. I want to embed this as an image in the component. Currently, I'm using something along the lines of:

    class SomeComponent extends React.Component {
        render() {
            var image = '<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" width="47.4" height="40.65" viewBox="21 18.5 158 135.5"><path d="M25,50 l150,0 0,100 -150,0 z" stroke-width="4" stroke="black" fill="rgb(128,224,255)" fill-opacity="1" ></path><path d="M25,50 L175,150 M25,150 L175,50" stroke-width="4" stroke="black" fill="black" ></path><g transform="translate(0,0)" stroke-width="4" stroke="black" fill="none" ><circle cx="100" cy="30" r="7.5" fill="black" ></circle><circle cx="70" cy="30" r="7.5" fill="black" ></circle><circle cx="130" cy="30" r="7.5" fill="black" ></circle></g></svg>';
            return (
                <div dangerouslySetInnerHTML={{ __html: image }} />
            )
        }
    }
    

    However using a property called dangerouslySetInnerHTML makes me pretty uneasy. Is there a more generally accepted (and safer) way to do this?

  • Soley
    Soley over 4 years
    This is the normal way to work with svgs. I have a string and want to generate an element out of it. the strings come from server
  • David Yeiser
    David Yeiser almost 4 years
    If you end up using this method you may need to wrap the SVG output in encodeURIComponent(): <img src={`data:image/svg+xml;utf8,${encodeURIComponent(image)}` />
  • Pablo LION
    Pablo LION over 2 years
    The code <img src={`data:image/svg+xml;utf8,${image}` /> is missing an } to close src property
  • Pablo LION
    Pablo LION over 2 years
    This is not working with this svg https://avatars.dicebear.com/api/jdenticon/:seed.svg I tested in the css-trick too
  • Pablo LION
    Pablo LION over 2 years
    This works with my case while <img src={data:image/svg+xml;utf8,${encodeURIComponent(image)}` />` doesn't. And my linter(in @types/node) says the btoa is deprecated. It's better to use Buffer.from(unescape(encodeURIComponent(image)),"latin1").to‌​String("base64");
  • Pablo LION
    Pablo LION over 2 years
    This latin1 is not necessary here, although btoa() uses latin1 instead of utf8.
  • Haseeb Saeed
    Haseeb Saeed over 2 years
    This worked in my case. Thanks a lot ser
  • Shivam
    Shivam over 2 years
    Perfect! way to go thanks :)