devserkan
devserkan

Reputation: 17608

How can I change the color of an SVG's child dynamically via props?

I'm importing the SVG with ReactComponent. Here is the related code:

import { ReactComponent as SuccessSVG } from "success.svg";

function SuccessIcon() {
  return <SuccessSVG />;
}

and this is the SVG itself:

<svg viewBox="0 0 233.3 233.3">
  <circle class="circle" cx="116.65" cy="116.65" r="116.65" />
  <path
    class="ok"
    d="M97.28,167.38l-33.7-33.7a6.34,6.34,0,1,1,9-9l29.21,29.21,76-76a6.34,6.34,0,1,1,9,9l-80.48,80.48A6.34,6.34,0,0,1,97.28,167.38Z"
    transform="translate(-8.51 -6.01)"
  />
</svg>

I want to change circle's and path's colors dynamically via props. Something like this obviously:

function SuccessIcon({ circleColor, pathColor) {
  ...
  ...
}

I've seen this question already. I don't want to use the accepted answer's solution. The other answer is closer to my desire but I couldn't make it work.

By the way, I don't want to use the SVG directly in my component.

Upvotes: 1

Views: 1786

Answers (3)

xialvjun
xialvjun

Reputation: 1248

I still have not found a good solution for this problem. But I found this:

https://www.w3.org/TR/SVGParamPrimer/ : With this, we can write a webpack-loader, wrap the svg as data-url in an object element on it's data property, so we can pass props. And we can detect the props type from where it's used in the svg file. Of course, we need to write our svg in that syntax: <rect fill="param(color) blue" stroke="param(outline) navy" />

https://tabatkins.github.io/specs/svg-params/: This is't official, don't know will it be implemented.

Since solution one need to write the svg in that syntax, maybe it has no difference from copy svg code to jsx file and do some small changes.

I have another idea:

import SvgCar from './car.svg';
const vdom = <SvgCar>
  <replace
    // path="a relative path point to an element or a property"
    path="children[2].fill"
    to={'red'} />
</SvgCar>;

// or
const vdom2 = <SvgCar>{vdom => {vdom.children[2].fill="red";}}</SvgCar>

But I think this way will import so much coupling. It's not a good idea.

Upvotes: 0

Dupocas
Dupocas

Reputation: 21307

You've already extracted SuccessSVG to its own component. Now you just need to pass via props the configurations you want to be dynamic

const SuccessSvg = ({ cx, cy }) => {
    return (
        <svg viewBox="0 0 233.3 233.3" >
            <circle class="circle" cx={cx} cy={cy} r="116.65" />
            <path
                class="ok"
                d="M97.28,167.38l-33.7-33.7a6.34,6.34,0,1,1,9-9l29.21,29.21,76-76a6.34,6.34,0,1,1,9,9l-80.48,80.48A6.34,6.34,0,0,1,97.28,167.38Z"
                transform="translate(-8.51 -6.01)"
            />
        </svg>
    )
}

const Component = () =>{
    return(
        <SuccessSvg color='blue' />
    )
}

Sadly you must consider the tradeoff. By declaring your svg as a stand alone component you write more code and have control over the customization. So that you can pass props which will be deeply applied inside your jsx structure

const CustomSVG = ({ svgProps, circleProps }) =>{
    return(
        <svg {...svgProps}>
            <circle {...circleProps} />
        </svg>
    )
}

This is why the OP for the linked question wraps the svg into a component, so that he can inject a custom className into the svg. Declarative components are awesome but you are limited by their API. In your case I don't see much alternatives, either abstract the structure to it's own component or use nested selectors.

Upvotes: 3

Dennis Vash
Dennis Vash

Reputation: 53874

If you don't want to use a component as @Dupocas suggested, your only way is to style your SVG:

// Example of CSS-in-JS , can be achieved with simple className.
const SVGContainer = styled.div`
  svg {
    path {
      fill: ${({ circleColor }) => circleColor};
    }
  }
`;

function SuccessIcon({ circleColor }) {
  return (
    <SVGContainer circleColor={circleColor}>
      <SuccessSVG />
    </SVGContainer>
  );
}

const App = () => {
  return <SuccessIcon circleColor="palevioletred" />;
};

Edit react-antd-styled-template

Upvotes: 2

Related Questions