Change svg stroke on hovering in React

707 Views Asked by At

In my React Project, I want to change the stroke-color of my svg on hovering. I've achieved this by importing svg as ReactCompnent and then sending prop conditionally (useState hook to determine whether its being hovered or not). But I want to know if there is any other simple way round for this? Because, it will make the code lengthy if done for multiple svg icons. My code is as following:

import { useState } from "react";
import { render } from "react-dom";
import "./index.css";
import { ReactComponent as Icon_5 } from "./icon_5.svg";

function App() {
  const [isHovered, setIsHovered] = useState(false);

  const handleMouseEnter = () => {
    setIsHovered(true);
  };

  const handleMouseLeave = () => {
    setIsHovered(false);
  };
  return (
    <>
      <div>
        <h1>Using with prop</h1>
        <Icon_5
          className="icon"
          stroke={isHovered ? "#FF0000" : "#00ade9"}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
        />
      </div>
    </>
  );
}

render(<App />, document.getElementById("root"));

I also tried applying color via css but the issue is that, each svg has different structure. For example, to completely change the stroke of this icon, I had to give it css this way:

.icon_1 path {
  stroke: green;
}

.icon_1 line {
  stroke: green;
}

.icon_1 rect {
  stroke: green;
}

And this varies for each svg icon. So, this approach desn't seem to be a fit for this case.

Here is the codesandbox example for this: https://codesandbox.io/s/svgfill-3-w7kl7n?file=/index.js:0-684

2

There are 2 best solutions below

2
vesper On

what's the issue with using CSS :hover?

it works with svg elements.

    <>
      <div>
        <h1>Using with prop</h1>
        <Icon_5 className="icon icon-5" />
      </div>
    </>
svg.icon-5 { stroke: #00ade9; }
svg.icon-5:hover { stroke: #FF0000; }

unless im missing something here

Link to the sandbox here

0
Aniket Pandey On

Just give the parent of svg component an id or class and remove the props and stroke from all svg(s). then in css you can use :

index.css:

.svg-container svg {
  stroke: gray;
}
.svg-container svg:hover {
  stroke: red;
}

/* svg#icon-5 {
  stroke: gray;
}

svg#icon-5:hover {
  stroke: red;
} */

App.js:

import "./index.css";
import { ReactComponent as Icon5 } from "./icon_5.svg";

function App() {
  return (
    <>
      <div class="svg-container">
        <Icon5 />
      </div>
    </>
  );
}

icon_5.svg:

<svg xmlns="http://www.w3.org/2000/svg" id="icon-5" width="27.802" height="24.704" viewBox="0 0 27.802 24.704">
  <g id="icon_calendar-clock" transform="translate(0.75 0.75)">
    <g id="Group_2511" data-name="Group 2511" transform="translate(-1 -1)">
      <g id="Group_15380" data-name="Group 15380">
        <line id="Line_297" data-name="Line 297" y2="4.013" transform="translate(6.744 1)" fill="none"  stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
        <line id="Line_298" data-name="Line 298" y2="4.013" transform="translate(16.685 1)" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
        <line id="Line_299" data-name="Line 299" x2="20.857" transform="translate(1 9.025)" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
      </g>
    </g>
    <rect id="Rectangle_3594" data-name="Rectangle 3594" width="3.017" height="3.017" transform="translate(3.57 11.395)" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
    <rect id="Rectangle_3595" data-name="Rectangle 3595" width="3.017" height="3.017" transform="translate(9.33 11.395)" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
    <g id="Group_2513" data-name="Group 2513" transform="translate(-1 -1)">
      <g id="Group_2527" data-name="Group 2527">
        <path id="Path_7197" data-name="Path 7197" d="M15.908,18.506a5.7,5.7,0,0,1,5.7-5.7,5.8,5.8,0,0,1,.739.053V5.943a2.756,2.756,0,0,0-2.756-2.756H3.756A2.756,2.756,0,0,0,1,5.943V19.585a2.755,2.755,0,0,0,2.756,2.756H17.4A5.67,5.67,0,0,1,15.908,18.506Z" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
        <path id="Path_7199" data-name="Path 7199" d="M21.6,12.809a5.7,5.7,0,1,1-5.7,5.7A5.7,5.7,0,0,1,21.6,12.809Z" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
        <path id="Path_7200" data-name="Path 7200" d="M23.753,19.369h-2.7v-3.1" fill="none"   stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
      </g>
    </g>
  </g>
</svg>

You can also give each svg tag(inside svg file) a separate id as well and use that to style. Even if you use svg as react component it is rendered at the end as plain svg only and css is applied to the end result so you can access it just as you would access a regular svg tag. I suggest you remove stroke from everywhere inside the svg, if the icons have a single color in all it's sub-parts(path, rect, etc..).Otherwise, you may run into issues using both external and internal styling on svgs. Also don't use render() directly like that, use: ReactDOM.createRoot()