Using Styled Components to change the color of an SVG's stroke
Asked Answered
F

4

12

I have an SVG I'm using as an <img> tag. Using Styled Components I am trying to get to a point where I change the stroke color upon hover.

I imported the SVG:

import BurgerOpenSvg from '../../images/burger_open.svg';

I Created a Styled Components for it:

   const BurgerImageStyle = styled.img`
    &:hover {     
        .st0 {
          stroke: red;
        }
    }
`;

And I use it:

<BurgerImageStyle alt="my-burger" src={BurgerOpenSvg}/>     

The result is, my SVG is displayed correctly, but no color change upon hovering.

Source for the SVG I use:

<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" 
     viewBox="0 0 38 28.4" style="enable-background:new 0 0 38 28.4;" xml:space="preserve">
<style type="text/css">
    .st0{fill:none;stroke:#221f1f;stroke-width:2;stroke-miterlimit:10;}
</style>
<g>
    <g id="XMLID_7_">
        <line class="st0" x1="0" y1="1" x2="38" y2="1"/>
    </g>
    <g id="XMLID_6_">
        <line class="st0" x1="0" y1="14.2" x2="38" y2="14.2"/>
    </g>
    <g id="XMLID_5_">
        <line class="st0" x1="0" y1="27.4" x2="38" y2="27.4"/>
    </g>
</g>
</svg>

The SVG Renders as follows:

enter image description here

Is it even possible to update the class on an SVG loaded in an <img> tag? or must it be inline <svg> tag?

Ferrol answered 20/6, 2019 at 19:36 Comment(7)
Do you see the hover styles in the browser?Mordant
.NYIBr:hover .st0{stroke:red;} . -- but no effect.Ferrol
Then the issue isn't with React. It's with your CSS. Does the element have that class on it?Mordant
yes. st0 is the class used to render the 3 lines in SVG.Ferrol
I meant .NYIBr. That's the operative class.Mordant
It does have it. But it has no effect.Ferrol
You might pull the rendered markup and CSS into a fiddle. We can't debug what we can't see. Again, not really a React thing.Mordant
R
11

If you are looking to avoid writing separate components or copying your raw SVG file, consider react-inlinesvg;

https://github.com/gilbarbara/react-inlinesvg

import React from "react";
import styled from "styled-components";
import SVG from "react-inlinesvg";
import radio from "./radio.svg";

interface SVGProps {
  color: string;
}

const StyledSVG = styled(SVG)<SVGProps>`
  width: 24px;
  height: 24px;
  & path {
    fill: ${({ color }) => color};
  }
`;

export default function App() {
  const color = "#007bff";
  return <StyledSVG color={color} src={radio} />;
}

Code Sandbox: https://codesandbox.io/s/stack-56692784-styling-svgs-iz3dc?file=/src/App.tsx:0-414

Rubbery answered 18/11, 2020 at 2:31 Comment(0)
F
11

So I looked into this. Turns out you cannot CSS style an SVG image you're loading using the <img> tag.

What I've done is this:

I inlined my SVG like this:

 <BurgerImageStyle x="0px" y="0px" viewBox="0 0 38 28.4">
      <line x1="0" y1="1" x2="38" y2="1"/>
      <line x1="0" y1="14.2" x2="38" y2="14.2"/>
      <line x1="0" y1="27.4" x2="38" y2="27.4"/>
 </BurgerImageStyle>

Then I used Styled Components to style BurgerImageStyle:

const BurgerImageStyle = styled.svg`
    line {
      stroke: black;
    }    
    &:hover {
      line {
        stroke: purple;
      }
    }     
`;

This worked.

Ferrol answered 20/6, 2019 at 20:37 Comment(0)
R
11

If you are looking to avoid writing separate components or copying your raw SVG file, consider react-inlinesvg;

https://github.com/gilbarbara/react-inlinesvg

import React from "react";
import styled from "styled-components";
import SVG from "react-inlinesvg";
import radio from "./radio.svg";

interface SVGProps {
  color: string;
}

const StyledSVG = styled(SVG)<SVGProps>`
  width: 24px;
  height: 24px;
  & path {
    fill: ${({ color }) => color};
  }
`;

export default function App() {
  const color = "#007bff";
  return <StyledSVG color={color} src={radio} />;
}

Code Sandbox: https://codesandbox.io/s/stack-56692784-styling-svgs-iz3dc?file=/src/App.tsx:0-414

Rubbery answered 18/11, 2020 at 2:31 Comment(0)
S
2

If you want to have some styling shared across multiple SVGs and you don't want to have an extra dependency on react-inlinesvg you can use this thing instead:

In src prop it accepts SVG React component

import styled from 'styled-components';
import React, { FC, memo } from 'react';

type StyledIconProps = {
  checked?: boolean;
};

const StyledIconWrapper = styled.div<StyledIconProps>`
  & svg {
    color: ${(p) => p.checked ? '#8761DB' : '#A1AAB9'};
    transition: 0.1s color ease-out;
  }
`;

export const StyledIcon = memo((props: StyledIconProps & { src: FC }) => {
  const { src, ...rest } = props;

  const Icon = src;

  return (
    <StyledIconWrapper {...rest}>
      <Icon/>
    </StyledIconWrapper>
  );
});

And then you can use it like:

import { StyledIcon } from 'src/StyledIcon';
import { ReactComponent as Icon } from 'assets/icon.svg';

const A = () => (<StyledIcon src={Icon} checked={false} />)
Smaragd answered 16/11, 2021 at 17:37 Comment(0)
E
0

In addition to what JasonGenX I propose the next case when you're using a SVG component (like one generated using SVGR). This is even on the styled-components documentation and in combination with its API it solves it seamlessly.

First import your icon

import React from 'react';
import styled from 'styled-components';
import YourIcon from '../../icons/YourIcon';

In my case I added a styled button like so:

const StyledButton = styled.button`
  ...
`;

// Provide a styled component from YourIcon
// You can also change the line for path and stroke for fill for instance
const StyledIcon = styled(YourIcon)`
  ${StyledButton}:hover & line {
    stroke: #db632e;
  }
`;

const YourButton = () => {
  return (
    <StyledButton>
      <StyledIcon /> Click me
    </StyledButton>
  );
};

export default YourButton;

After that you'll see your icon changes its color.

Ensample answered 16/9, 2022 at 9:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.