Embedding SVG into ReactJS - javascript

Is it possible to embed SVG markup into a ReactJS component?
render: function() {
return (
<span>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmln ...
</span>
);
}
Results in the error:
Namespace attributes are not supported. ReactJSX is not XML.
What is the lightest way of doing this. Using something like React ART is way overkill for what I'm trying to do.

Update 2016-05-27
As of React v15, support for SVG in React is (close to?) 100% parity with current browser support for SVG (source). You just need to apply some syntax transformations to make it JSX compatible, like you already have to do for HTML (class → className, style="color: purple" → style={{color: 'purple'}}). For any namespaced (colon-separated) attribute, e.g. xlink:href, remove the : and capitalize the second part of the attribute, e.g. xlinkHref. Here’s an example of an svg with <defs>, <use>, and inline styles:
function SvgWithXlink (props) {
return (
<svg
width="100%"
height="100%"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<style>
{ `.classA { fill:${props.fill} }` }
</style>
<defs>
<g id="Port">
<circle style={{fill:'inherit'}} r="10"/>
</g>
</defs>
<text y="15">black</text>
<use x="70" y="10" xlinkHref="#Port" />
<text y="35">{ props.fill }</text>
<use x="70" y="30" xlinkHref="#Port" className="classA"/>
<text y="55">blue</text>
<use x="0" y="50" xlinkHref="#Port" style={{fill:'blue'}}/>
</svg>
);
}
Working codepen demo
For more details on specific support, check the docs’ list of supported SVG attributes. And here’s the (now closed) GitHub issue that tracked support for namespaced SVG attributes.
Previous answer
You can do a simple SVG embed without having to use dangerouslySetInnerHTML by just stripping the namespace attributes. For example, this works:
render: function() {
return (
<svg viewBox="0 0 120 120">
<circle cx="60" cy="60" r="50"/>
</svg>
);
}
At which point you can think about adding props like fill, or whatever else might be useful to configure.

If you just have a static svg string you want to include, you can use dangerouslySetInnerHTML:
render: function() {
return <span dangerouslySetInnerHTML={{__html: "<svg>...</svg>"}} />;
}
and React will include the markup directly without processing it at all.

According to a react developer, you dont need the namespace xmlns. If you need the attribute xlink:href you can use xlinkHref from react 0.14
Example
Icon = (props) => {
return <svg className="icon">
<use xlinkHref={ '#' + props.name }></use>
</svg>;
}

If you want to load it from a file, you may try to use React-inlinesvg - that's pretty simple and straight-forward.
import SVG from 'react-inlinesvg';
<SVG
src="/path/to/myfile.svg"
preloader={<Loader />}
onLoad={(src) => {
myOnLoadHandler(src);
}}
>
Here's some optional content for browsers that don't support XHR or inline
SVGs. You can use other React components here too. Here, I'll show you.
<img src="/path/to/myfile.png" />
</SVG>

You can import svg and it use it like a image
import chatSVG from '../assets/images/undraw_typing_jie3.svg'
And ise it in img tag
<img src={chatSVG} className='iconChat' alt="Icon chat"/>

The best way I found was this site
https://react-svgr.com/playground/
You copy an svg and it give you a react functional component with the svg transformed to react code.

There is a package that converts it for you and returns the svg as a string to implement into your reactJS file.
https://www.npmjs.com/package/convert-svg-react

Related

How to load SVG from file/url and display as React component

I want to load an SVG from a file or through a URL and then use it as a React component.
I've tried many ways, but I can't figure out to do that correctly.
Can someone give me a hint to achieve it?
Currently I'm creating components this way, but as I mentioned, it should be at runtime loaded from a file instead:
const SvgComponent = (props) => (
<svg
height={800}
width={800}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
xmlSpace="preserve"
{...props}
>
<path
fill={props.color || '#000'}
d="M23 24H3v-2h18V6.4L16.6 2H5v7H3V0h14.4L23 5.6z"
/>
<path
fill={props.color || '#000'}
d="M22 8h-7V2h2v4h5zM4.8 15.4l-3-4.4h2.3L6 13.9 7.9 11h2.3l-3 4.4 3.1 4.6H8l-2-3.1L4 20H1.7l3.1-4.6z"
/>
</svg>
)
For a URL, you'd just use the image tag <img src={svgUrl} ...>
I suppose you could also fetch or read from file the SVG data as you would any other data and render it {svgData}.
You can use FileReader's readAsDataURL and set the result to an image's src, or read the file (or fetch the data) an use <img src={URL.createObjectURL(photoData)} alt={photoName} /> or similar.

How can I make URL action work from a vue component

I'm working on a small project with ASP.NET MVC and VueJs. I created some components and added them to the Project.js file like so:
<div class="card_four">
<a href="#Url.Action('LoanRequest', 'LoanOrigination')">APPLY NOW
<i class="icon">
<svg width="8" height="13" viewBox="0 0 8 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.584961 11.5841L5.17099 6.99807L0.584961 2.41205L1.99896 0.998047L7.99899 6.99807L1.99896 12.9981L0.584961 11.5841Z" fill="white"></path>
</svg>
</i>
</a>
<p>No collateral Needed</p>
</div>
When I clicked on the link, the asp.net MVC URL link broke. How can I rewrite this URL action in the vuejs compatible format as I'm new to VueJs
The # symbol is razor syntax. In js frameworks you no longer have the # symbol. In the case of urls, you usually just use strings. Just use
<a href='/loanorigination/loanrequest'>APPLY NOW
Or for more flexibility, look up Vue Properties and pass it in to your component as a property called something like loanUrl and use {loanUrl} in place of the string above.

Javascript Parser Error While Trying To Include SVG Element as Functional React Component

I am building a React application that serves as a community directory for a global music scene I am a part of. I am trying to follow an SVG icon strategy that wraps the SVG code with a functional React component so that you can then import the icon into the component that uses it as a React component (NYT Article that outlines the strategy). This strategy is working for some components, but for many, I am getting parsing errors along the lines of:
./src/Components/LargeScopeComponents/ArtistProfileDisplay/TopBar/SVG/ListViewIcon/ListViewIconDarkTheme.js
Line 4: Parsing error: Unexpected token, expected "}"
2 |
3 | const ListViewIconDarkTheme = () => (
> 4 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57.72 53.2"><defs><style>.cls-1{fill:#fff}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M15.38 41.72h42.35V53.2H15.38zM.41 41.72h10.86V53.2H.41zM15.15 20.86H57.5v11.48H15.15zM0 20.86h10.86v11.48H0zM15.15 0H57.5v11.48H15.15zM0 0h10.86v11.48H0z"/></g></g></svg>
| ^
5 | );
6 |
7 | export default ListViewIconDarkTheme;
I'm not completely sure why the javascript parser doesn't like the SVG syntax, because this strategy is working for other components. For example, I used the same strategy to create this functional component, and the javascript parser did not complain when I included it in a card component I use it in.
import React from 'react';
const CircleUpIcon_LightTheme = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 83.28 83.28"><g data-name="Layer 2"><g data-name="Layer 1"><circle cx="41.64" cy="41.64" r="38.64" fill="none" stroke="#000" stroke-miterlimit="10" stroke-width="6"/><path d="M41.64 20.7L20.92 56.58h41.43L41.64 20.7z"/></g></g></svg>
);
export default CircleUpIcon_LightTheme;
I'm wondering if there are any workarounds to this or modifications I could make to the SVG syntax to please the javascript parser. Any help would be much appreciated :)
The problem is that svg syntax clashes with jsx. React supports most svg element types (see: https://reactjs.org/docs/dom-elements.html#all-supported-svg-attributes), but for things like <style> you need to make the children react compatible. For example:
<style>{
`.cls-1{fill:#fff}`
}</style>
Notice the extra braces, and converting the child to a string. This should fix your first error.
The style tag in ListViewIconDarkTheme is causing the problem.
Why don't you try this? (see style attribute)
const ListViewIconDarkTheme = () => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 57.72 53.2"><g
id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path
class="cls-1" style="fill:#fff" d="M15.38 41.72h42.35V53.2H15.38zM.41
41.72h10.86V53.2H.41zM15.15 20.86H57.5v11.48H15.15zM0
20.86h10.86v11.48H0zM15.15 0H57.5v11.48H15.15zM0 0h10.86v11.48H0z"/></g></g> .
</svg>
);
export default ListViewIconDarkTheme;

How do you fetch an svg file and use it as <svg>?

My Question:
How do I fetch a .svg file and use it with <svg> tag, so I could also interactively style it (on hover for example), and not like I did in my example with <img> tag?
Goal:
Create a thumbnail view that shows the flag of each country (149 in number) on my main route '/'
Problem:
I've set a static route in my Express code, so all the flags to be available.
server.js:
app.use('/flags', express.static(FLAGS_FILES));
app.get('/flags', async (req, res) => {
let myFlags;
await readdir('data/flag-icons/')
.then((flags) => myFlags = flags)
.catch(err => console.log('Error:', err))
res.json(myFlags)
});
On the client-side, I want to fetch each .svg file and inline it into my Component.
My React Component look like this:
MapThumbnail Component:
render(){
const {flagcode} = this.props;
const filepath = `/flags/${mapcode}/vector.svg`;
console.log('filepath:', filepath);
return (
<MapThumbnailStyle>
<img src={filepath}
alt={flagcode}
title={flagcode}
width='40px'
height='40px'
/>
</MapThumbnailStyle>
);
}
https://github.com/gilbarbara/react-inlinesvg looks like it would help you.
You can in most of the cases directly inline the contents of the SVG file inside the render function, like -
TL;DR
react-svg-loader
It provides a webpack-loader, a CLI, and a few other tools that convert an SVG file to a React Component which contains the SVG file's contents inline in the render method.
The gist of the idea is the following -
vector.svg
<svg>
<circle cx="50" cy="50" r="25" stroke-width="5" />
</svg>
Vector.js
Now create a vector.js and just copy the contents of the SVG like below
import React from "react"
export default () => (
<svg>
<circle cx="50" cy="50" r="25" stroke-width="5" />
</svg>
);
and you can hand edit some elements here or assign ids to animate them.
MapThumbnail Component
And in your MapThumbnail component, you can import it as JS and style the SVG the way you want -
import Vector from "./path/to/vector";
// ...
// ...
render() {
return <div>
<Vector />
</div>
}
This conversion of SVG to a React Component can be automated to some levels and is provided by a few tools - one such tool is react-svg-loader. Disclaimer - I'm the author of react-svg-loader.
Best way is Lazy load the SVG component
const SvgComponent = React.lazy(() => import('../filepath.svg'))
return (
<React.Suspense fallback={<p>Loading...</P>}>
<SvgComponent />
</React.Suspense>
)
And this solution also works in React Native along with react-native-svg-transformer module.

How to make Meteor React inline svg element click link to page without refresh whole page

I need some guidance. First time here, so maybe some of the "terms" I'm using is unusual, please bear with me.
I am trying to achieve as what title said in JSX (using Meteor plafform). Currently if I use xlinkHref in svg, when I click the element, it will open the link but resulted in refreshing the whole page. While outside svg (using href), it will open the link without refreshing whole page. Code example:
import React, { Component } from 'react';
export default class App extends Component {
render() {
return (
<div>
<svg>
<a xlinkHref="/nextpage"> //clicking this will refresh whole page
<circle cx="50" cy="50" r="40" fill="red" />
</a>
<circle cx="150" cy="50" r="40" fill="yellow" />
</svg>
Click to Next Page //clicking this will not refresh
</div>
)
}
Note that if I wrap the "a" tag outside svg tag, it will definitely work, but I just need the red circle to be clickable.
I have been thinking about breaking the circle elements into separate svg then wrap with "a" tag, but lets just say I am unable to do that, how can I still click an svg element to open link without refreshing whole page?
Edit: fixed some code typo
You can add onclick event handler on your circles.
export default class App extends Component {
onCircleClick(event) {
event.preventDefault();
console.log('Inside on circle click');
window.location='/nextpage';
}
render() {
return (
<div>
<h1> Testing svg tag </h1>
<svg>
<circle onClick= {this.onCircleClick} cx="50" cy="50" r="40" fill="red"/>
<circle onClick= {this.onCircleClick} cx="150" cy="50" r="40" fill="yellow" />
</svg>
</div>
);
}
}
With React Router
By using browserHistory from React Router you will be able to open the link without refreshing the whole page.
import React, { Component } from 'react';
import { browserHistory } from 'react-router';
export default class App extends Component {
handleClick(link) {
browserHistory.push(link);
}
render() {
return (
<div>
<svg>
<a onClick={this.handleClick.bind(null, "/nextpage")}>
<circle cx="50" cy="50" r="40" fill="red" />
</a>
</svg>
</div>
)
}
}
browserHistory uses the HTML5 History API when available, and falls back to full refreshes otherwise. browserHistory requires additional configuration on the server side to serve up URLs, but is the generally preferred solution for modern web pages.
More about React Router's API:
https://github.com/ReactTraining/react-router/blob/master/docs/API.md#browserhistory
With history.pushState()
If you can't use React Router, you can still use HTML5's API pushstate to change the route: https://developer.mozilla.org/en-US/docs/Web/API/History_API
history.pushState(name, title, "nextpage");
Note that history is not compatible with old browser: http://caniuse.com/#search=history
With a router for Meteor
It should be able to use history.pushState(). If not the router should have the equivalent of pushState the same way React Router does.

Categories

Resources