Turning an SVG string into an image in a React component
Asked Answered
T

7

43

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?

Turbofan answered 4/7, 2017 at 8:17 Comment(1)
There is a good answer to a similar question here: https://mcmap.net/q/130432/-embedding-svg-into-reactjsSitting
A
51

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,${encodeURIComponent(image)}`} />
            </div>
        )
    }
}

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

Alsatia answered 18/10, 2019 at 18:28 Comment(4)
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)}` />Chinch
The code <img src={`data:image/svg+xml;utf8,${image}` /> is missing an } to close src propertyHutto
This is not working with this svg https://avatars.dicebear.com/api/jdenticon/:seed.svg I tested in the css-trick tooHutto
@DavidYeiser encodeURIComponent() worked for me, thanks!!Violaviolable
G
15

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)));
Gearbox answered 22/11, 2019 at 9:37 Comment(4)
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").toString("base64");Hutto
This latin1 is not necessary here, although btoa() uses latin1 instead of utf8.Hutto
This worked in my case. Thanks a lot serPentathlon
Perfect! way to go thanks :)Francescafrancesco
V
10

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} />;
Vivianaviviane answered 13/8, 2020 at 10:34 Comment(0)
K
7

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...

Kala answered 2/10, 2020 at 15:15 Comment(3)
Despite the attention it received, this is a great solution. No extra library and no unsafe HTML. Worth mentioning, useEffect is required here, otherwise ref.current will be undefined and your app will explode at runtime.Bearden
@Bearden can you please elaborate on the useEffect being required? I'm attempting to follow this solution as I need the SVG to be inline but my app IS in fact exploding at runtime. It seems to happen as soon as the function is called: ``` const createSVG = (svg) => { const svgWrapper = useRef(); useEffect(() => { svgWrapper.current.innerHTML = svg; }, []); return { svgWrapper } }```Aciculate
In your case, the issue may be with the svg parameter as it is passed down to the function. It is possibly undefined at the time it is assigned to svgWrapper.current.innerHTML. A solution would be to add svg to the useEffect dependency array. Also, add a safeguard for svg inside useEffect so that an undefined value doesn't get assigned svgWrapper.current.innerHTML.Bearden
B
6

In this way, I succeeded.

const svgStr = "<svg></svg>";
const svg = new Blob([svgStr], { type: "image/svg+xml" });
const url = URL.createObjectURL(svg);
<img src={url} />
Brunn answered 20/3, 2022 at 11:8 Comment(0)
J
-1

I know the question is about the string of an svg object, but in many cases you can use the svg object directly and even change it dynamically, which can be a cleaner approach in comparison to using string.


Using the svg object directly:

import React from 'react';

function CustomSvgObject({ }) {
    return <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>
}

You can also dynamically change the svg, like the className property or the color of the svg elements:

import React from 'react';

function CustomSvgObject({ className, color }) {
    return <svg className={className} 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={color} fill="rgb(128,224,255)" fill-opacity="1" ></path><path d="M25,50 L175,150 M25,150 L175,50" stroke-width="4" stroke={color} fill={color} ></path><g transform="translate(0,0)" stroke-width="4" stroke={color} fill="none" ><circle cx="100" cy="30" r="7.5" fill={color} ></circle><circle cx="70" cy="30" r="7.5" fill={color} ></circle><circle cx="130" cy="30" r="7.5" fill={color} ></circle></g></svg>
}
Jolee answered 21/2, 2023 at 16:3 Comment(0)
I
-5

I would store the svg image in a separate folder(assets), and import the image into the react component

Something like this:

SomeComponent.js:

import { SampleImage } from '../assets/SomeFile';

class SomeComponent extends React.Component {
    render() {

        return (
            <div>
              <img src={SampleImage} />
            <div/>
        )
    }
}

SomeFile.svg:

<?xmlns="http://www.w3.org/2000/svg" version="1.2"encoding="UTF-8"?>

 <svg 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>
Infraction answered 5/5, 2019 at 1:4 Comment(2)
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 serverGearbox
Is it possible to set style on this dynamicaly render svg?Conflagration

© 2022 - 2024 — McMap. All rights reserved.