Reputation: 2890
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?
Upvotes: 44
Views: 46086
Reputation: 3141
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>
}
Upvotes: -1
Reputation: 838
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/
Upvotes: 54
Reputation: 1008
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} />
Upvotes: 6
Reputation: 23737
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...
Upvotes: 7
Reputation: 767
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} />;
Upvotes: 10
Reputation: 1776
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)));
Upvotes: 15
Reputation: 544
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>
Upvotes: -5