Using SVG files as a React Component in a CRA app - reactjs

I'm trying to import an svg file but as a react component but I can't see the actual icon. I expect to see a Home Icon but all I'm seeing is a black-filled figure.
What I tried
import {ReactComponent as Home} from '../assets/svgs/home.svg';
import {List, ListItem, ListItemIcon} from '#material-ui/core';
<List>
{['Home', 'Video', 'Music', 'Sound', 'Painting', 'Images', 'Heritage', 'Fashion'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <Home/> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
<List/>
My SVG File
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet" width="25px" height="25px">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M0 2560 l0 -2560 2560 0 2560 0 0 2560 0 2560 -2560 0 -2560 0 0
-2560z"/>
</g>
</svg>
What I'm getting
What am I doing wrong? I'd really appreciate your help.

I just checked your SVG on this site: http://svg.enshrined.co.uk/
The SVG itself is broken I suggest you find another.
Your SVG

Related

How is SVG format images are set in React.Js?

I have been working on my college website and to make things easier and time saving, I downloaded a template and completed half of the requirements of my project but I got some issues while finding new image formats like svg as I didn't learn about handling them earlier.
Can anyone tell me how to use use svg format images because they are written in xml...!!
I surfed through many websites still can't get out my problem ,please help me out.
The easiest way to use SVG images in react is by importing them and using them in the <img /> src.
import myLogo from '/path/to/image.svg'
const Button = () => {
return (
<button>
<img src={myLogo} alt="SVG logo image"/>
</button>
);
}
Setting them as background images and such with CSS:
.container {
background-image: url(/path/to/image.svg);
}
Converting into JSX:
Regular SVG tag:
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
/>
</svg>
React:
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
/>
</svg>
SVG as a React Component
/path/to/LeftArrow.jsx
export const LeftArrow = () =>{
return(
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M11 19l-7-7 7-7m8 14l-7-7 7-7"
/>
</svg>
)
};
import { LeftArrow } from './path/to/LeftArrow.jsx'
export const Button = () =>{
return(
<button>
<LeftArrow />
</button>
)
};
Use SVGR as data-url
This is what I do as it allows me to use SVGs as inline elements.
npm install svg-url-loader --save-dev
webpack.config.js
const webpack = require('webpack');
module.exports = {
entry: './src/index.js',
module: {
rules: [
{
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
limit: 10240,
// make all svg images to work in IE
iesafe: true,
},
},
],
},
],
},
};
import myLogo from '/path/to/image.svg'
const Button = () => {
return (
<button>
<img src={myLogo} alt="SVG logo image"/>
</button>
);
};
This will compile it into a URL like:
<img src="wes49idjdur935jujd8907jdh.svg" alt="SVG logo image" />
Most of the time when using React, you're likely going to want to render SVGs conditionally. A common example would be the "hamburger" menu (which changes upon click). I went ahead and included an example of that:
Hamburger.jsx
import React from 'react';
export const Hamburger = () =>{
const [ isOpen, setisOpen ] = React.useState(false)
return(
<React.Fragment>
{ isOpen ? (
<button
onClick={()=>setisOpen(false)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
):( <button
onClick={()=>setisOpen(true)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={2}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</button>
)
}
</React.Fragment>
)
};
Additional CSS to make the SVG appear properly:
svg {
height: 40px;
width: 40px;
}
Actually is very easy. You can configure webpack (or the bundler you are using) to import svgs directly as react components. You can use an img tag and add the route to the svg, it should render it directly as an image. An also you can rely on a library . All in all, even though they are written in xml, you can think about svg as images

How to apply onClick event on Svg file on React?

I made a component for a Svg and I'm trying to apply onClick event so I change the state, but it doesnt work for some reason, I'm not sure what I'm doing wrong. I tried applying the onCLick on too , but it doesnt work either.
my code
import React, { useState } from "react";
import './style.scss'
const AverageSvg=() => {
const [active, setActive] = useState(false);
return (
<svg className="average" onClick={() => setActive(false)}
xmlns="http://www.w3.org/2000/svg"
width="170.539"
height="51.974"
viewBox="0 0 170.539 51.974"
>
<g data-name="The average" transform="translate(-1223 -2501)" >
<g className={active ? "clicked-fill" : "fill "}
// fill="none"
stroke="#707070"
strokeWidth="1"
data-name="Rectangle 60"
transform="translate(1223 2501)"
>
<rect
width="170.539"
height="51.974"
stroke="none"
rx="25.987"
></rect>
<rect
width="169.539"
height="50.974"
x="0.5"
y="0.5"
rx="25.487"
></rect>
</g>
<text className="text"
// fill="#464646"
data-name="The average"
fontFamily="ArialMT, Arial"
fontSize="17"
transform="translate(1261 2532)"
>
<tspan x="0" y="0" >
The average
</tspan>
</text>
</g>
</svg>
);
}
export default AverageSvg;
Have you tried wrapping the svg with another tag like a div or span and attaching the onClick on that wrapper ?
Also inside the setActive() you should pass true instead of false.
you can warp the SVG in another tag as Alexader says, also you can pass the event to the child element in the SVG tag directly and it will work. but you should be aware of the browser compatibility
check this link from MDN web Docs
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/onclick
for example, this is an SVG delete icon
export const Delete = ({ ...props }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" height="24" width="24" className={props?.className} >
<path onClick={props?.onClick} d="M7 21q-.825 0-1.412-.587Q5 19.825 5 19V6H4V4h5V3h6v1h5v2h-1v13q0 .825-.587 1.413Q17.825 21 17 21Zm2-4h2V8H9Zm4 0h2V8h-2Z" />
</svg>
)
}
when you call it just <Delete onClick={yourEvent} />
the onClick will apply on the <path/> tag

Menu not rendering fully in svg

I'm trying to render a drop-down menu inside an svg, such as:
However, when I click on it, the bottom of the menu is chopped off:
How can I fix this?
<svg>
<foreignObject x={0} y={0} width={"100%"} height={"100%"}>
<Menu>
<MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
Actions
</MenuButton>
<MenuList>
<MenuItem>Download</MenuItem>
<MenuItem>Create a Copy</MenuItem>
<MenuItem>Mark as Draft</MenuItem>
<MenuItem>Delete</MenuItem>
<MenuItem>Attend a Workshop</MenuItem>
</MenuList>
</Menu>
</foreignObject>
</svg>
Here's the codesandbox:
https://codesandbox.io/s/chakra-button-forked-7ig5f?file=/src/App.js
You can try adding height or view box attributes to the SVG element. The SVG is most likely being cut off for this reason.
<svg viewBox="0 0 500 500">
or
<svg height="500px">

hiding react svg component with linearGradient in Header also hides the svg outside of Header

I have a svg react component with linearGradient that I render in my Header component and my main component. When I set display: none style to the Header component, I expect the svg in the Header component to be hidden and the svg in the main component to be visible. When I use svg without linearGradient, I get the expected behavior of svg hidden in Header and visible in main component. However, if the svg uses linearGradient, then setting display: none to the Header hides the svg in the Header and the main component.
To reproduce the issue, I have 2 react components PythonIcon (using linearGradient) and WebpackIcon (not using linearGradient). In my App.js, I render the Header and also render both PythonIcon and WebpackIcon in my main component:
import React from "react";
import Header from "./Header";
import PythonIcon from "./PythonIcon";
import WebpackIcon from "./WebpackIcon";
function App() {
return (
<div>
<Header />
<main style={{ marginTop: "1rem", border: "1px solid green" }}>
<h1>
<PythonIcon width="20" height="20" />
Python
</h1>
<h1>
<WebpackIcon width="20" height="20" />
Webpack
</h1>
</main>
</div>
);
}
export default App;
In my Header component, I also render PythonIcon and WebpackIcon
import React from "react";
import PythonIcon from "./PythonIcon";
import WebpackIcon from "./WebpackIcon";
export default function Header() {
return (
<header style={{ border: "1px solid red", display: "block" }}>
<h1>
<PythonIcon width="20" height="20" />
Python
</h1>
<h1>
<WebpackIcon width="20" height="20" />
Webpack
</h1>
</header>
);
}
And the page looks like this:
But once I change display: block to display: none in the Header component, the page looks like this:
Notice that the webpack icon (which doesn't use linearGradient) is correctly showing in main component, but the python icon (which uses linearGradient) is not.
This was unexpected. Am I doing something wrong? Is there any way to hide the python icon in the header without having it hide the python icon in the main component? I am trying to create a responsive navbar using css media-query to show/hide navbar. Unfortunately, hiding the navbar w/ svg icons also hides the svg icon in my other components that I render.
I've provided a minimal reproduction at https://github.com/kimbaudi/react-svg-lineargradient-issue
So I found a solution to this problem. The reason this is happening is because svgs using linearGradient or radialGradient contain ids and when you render multiple svgs in a page, it causes weird things to happen because the DOM expects there to be a unique id.
So my previous PythonIcon react svg component had hardcoded ids and looked like this:
import React from "react";
const PythonIcon = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 64 64"
width="64"
height="64"
{...props}
>
<linearGradient
id="pythonLinearGradient1"
x1="611.11"
x2="720.13"
y1="-280.51"
y2="-374.3"
gradientTransform="matrix(.32049 0 0 -.32362 -197.36 -88.951)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#5A9FD4" offset="0" />
<stop stopColor="#306998" offset="1" />
</linearGradient>
<path
fill="url(#pythonLinearGradient1)"
strokeWidth=".56976"
d="m31.344 0c-2.6209 0-5.1278 0.2279-7.2929 0.62674-6.4383 1.1395-7.6348 3.5325-7.6348 7.9197v5.8116h15.27v1.9372h-21.024c-4.4441 0-8.3185 2.6779-9.572 7.7488-1.4244 5.8116-1.4814 9.458 0 15.498 1.0825 4.5011 3.7034 7.7488 8.1476 7.7488h5.2418v-6.9511c0-5.0139 4.3872-9.515 9.572-9.515h15.27c4.2732 0 7.6348-3.4755 7.6348-7.7488v-14.529c0-4.1593-3.4755-7.236-7.6348-7.9197-2.6209-0.45581-5.3558-0.62674-7.9767-0.62674zm-8.2615 4.672c1.5953 0 2.8488 1.3104 2.8488 2.9058 0 1.5953-1.3104 2.9058-2.8488 2.9058-1.5953 0-2.8488-1.3104-2.8488-2.9058 0-1.5953 1.2535-2.9058 2.8488-2.9058z"
/>
<linearGradient
id="pythonLinearGradient2"
x1="762.27"
x2="723.34"
y1="-431.07"
y2="-375.99"
gradientTransform="matrix(.32049 0 0 -.32362 -197.36 -88.951)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#FFD43B" offset="0" />
<stop stopColor="#FFE873" offset="1" />
</linearGradient>
<path
fill="url(#pythonLinearGradient2)"
strokeWidth=".56976"
d="m48.836 16.352v6.7802c0 5.2418-4.4441 9.6859-9.572 9.6859h-15.213c-4.1593 0-7.6348 3.5895-7.6348 7.7488v14.529c0 4.1593 3.5895 6.5523 7.6348 7.7488 4.843 1.4244 9.458 1.6523 15.27 0 3.8744-1.1395 7.6348-3.3616 7.6348-7.7488v-5.8116h-15.27v-1.9372h22.904c4.4441 0 6.0964-3.0767 7.6348-7.7488 1.5953-4.786 1.5384-9.4011 0-15.498-1.0825-4.4441-3.1907-7.7488-7.6348-7.7488zm-8.5464 36.807c1.5953 0 2.8488 1.3104 2.8488 2.9058 0 1.5953-1.3104 2.9058-2.8488 2.9058-1.5953 0-2.8488-1.3105-2.8488-2.9058-0.05698-1.5953 1.2535-2.9058 2.8488-2.9058z"
/>
</svg>
);
export default PythonIcon;
Now I append a random string (I'm just using Math.random() for demo purpose) to the ids and I no longer have the issue of Python icon disappearing on main body when I hide Header. Here is the updated PythonIcon component:
import React from "react";
const PythonIcon = (props) => {
const rand1 = Math.random()
const rand2 = Math.random()
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 64 64"
width="64"
height="64"
{...props}
>
<linearGradient
id={`pythonLinearGradient1-${rand1}`}
x1="611.11"
x2="720.13"
y1="-280.51"
y2="-374.3"
gradientTransform="matrix(.32049 0 0 -.32362 -197.36 -88.951)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#5A9FD4" offset="0" />
<stop stopColor="#306998" offset="1" />
</linearGradient>
<path
fill={`url(#pythonLinearGradient1-${rand1})`}
strokeWidth=".56976"
d="m31.344 0c-2.6209 0-5.1278 0.2279-7.2929 0.62674-6.4383 1.1395-7.6348 3.5325-7.6348 7.9197v5.8116h15.27v1.9372h-21.024c-4.4441 0-8.3185 2.6779-9.572 7.7488-1.4244 5.8116-1.4814 9.458 0 15.498 1.0825 4.5011 3.7034 7.7488 8.1476 7.7488h5.2418v-6.9511c0-5.0139 4.3872-9.515 9.572-9.515h15.27c4.2732 0 7.6348-3.4755 7.6348-7.7488v-14.529c0-4.1593-3.4755-7.236-7.6348-7.9197-2.6209-0.45581-5.3558-0.62674-7.9767-0.62674zm-8.2615 4.672c1.5953 0 2.8488 1.3104 2.8488 2.9058 0 1.5953-1.3104 2.9058-2.8488 2.9058-1.5953 0-2.8488-1.3104-2.8488-2.9058 0-1.5953 1.2535-2.9058 2.8488-2.9058z"
/>
<linearGradient
id={`pythonLinearGradient2-${rand2}`}
x1="762.27"
x2="723.34"
y1="-431.07"
y2="-375.99"
gradientTransform="matrix(.32049 0 0 -.32362 -197.36 -88.951)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#FFD43B" offset="0" />
<stop stopColor="#FFE873" offset="1" />
</linearGradient>
<path
fill={`url(#pythonLinearGradient2-${rand2})`}
strokeWidth=".56976"
d="m48.836 16.352v6.7802c0 5.2418-4.4441 9.6859-9.572 9.6859h-15.213c-4.1593 0-7.6348 3.5895-7.6348 7.7488v14.529c0 4.1593 3.5895 6.5523 7.6348 7.7488 4.843 1.4244 9.458 1.6523 15.27 0 3.8744-1.1395 7.6348-3.3616 7.6348-7.7488v-5.8116h-15.27v-1.9372h22.904c4.4441 0 6.0964-3.0767 7.6348-7.7488 1.5953-4.786 1.5384-9.4011 0-15.498-1.0825-4.4441-3.1907-7.7488-7.6348-7.7488zm-8.5464 36.807c1.5953 0 2.8488 1.3104 2.8488 2.9058 0 1.5953-1.3104 2.9058-2.8488 2.9058-1.5953 0-2.8488-1.3105-2.8488-2.9058-0.05698-1.5953 1.2535-2.9058 2.8488-2.9058z"
/>
</svg>
);
};
export default PythonIcon;
and here is the screenshot showing the expected behavior of the Python icon not being hidden when the Header is hidden using display: none

Why isn't fill working on my svg when using props?

I expect the svg to turn red but it isn't changing at all from the color #D8D8D8. I see the fill in the svg and if I turn this into a parameter I can use it then, but I'd rather just set the fill using React props so I can set a hover via my css:
The svg code (which I got my my Sketch export):
<svg width="57px" height="33px" viewBox="0 0 57 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Rectangle</title>
<g id="Design" stroke="none" stroke-width="1" fill-rule="evenodd">
<g id="Mobile-Copy-6" transform="translate(-196.000000, -69.000000)" fill="#D8D8D8">
<rect id="Rectangle" x="196" y="69" width="57" height="33"></rect>
</g>
</g>
</svg>
My react code:
import { ReactComponent as Logo } from "../../assets/rect.svg";
export const Example = () => {
return (
<div>
<Logo fill="#C94141" />
</div>
);
};
I used create-react-app with the typescript template and the Logo is identified as the type:
React.FunctionComponent<React.SVGProps<SVGSVGElement>
you need to create you Logo as a component with passing props to it which you want to keep dynamic. Try creating a component like:
export default ({ fill = '#D8D8D8' }) => (
<svg width="57px" height="33px" viewBox="0 0 57 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Rectangle</title>
<g id="Design" stroke="none" stroke-width="1" fill-rule="evenodd">
<g id="Mobile-Copy-6" transform="translate(-196.000000, -69.000000)" fill={fill}>
<rect id="Rectangle" x="196" y="69" width="57" height="33"></rect>
</g>
</g>
</svg>
);
and then use this component simply by
import Logo from "../../components/logo.js";
export const Example = () => {
return (
<div>
<Logo fill="#C94141" />
</div>
);
};

Resources