When you want to dynamically change the color of svg icons, like if you're building a theme, sometimes it is difficult to dynamically pass the value especially in case you are using svg files directly exported from figma. SVG files have their preset value with fill, stroke, maybe shadow value with filter, with <g>, <circle>, <rect> ... AND different structures for each icon.

I found a stable way to implement this to svgs library using CSS filter so I'd like to share.

What is filter?

Filter is a css attribute that achieves varying visual effects (sort of like Photoshop filters for the browser). (You can see more explanation here)
Since it modifies the visible colors in svg, we do not need to modify each element (e.g. <g>, ) under <svg> element.

How do I set the hex color with filter?

This article/codepen gives a great example. By adjusting each filter value, you can achieve the appearance that looks like the desired hex color.

<iframe height="600" src="https://codepen.io/sosuke/embed/Pjoqqp?height=600&default-tab=result&embed-version=2" scrolling="no" frameborder="no" allowtransparency="true" loading="lazy" style="width: 100%;"> </iframe>

How can I use it with React component?

All you need is to add a way to convert the hex value to CSS filter value in your project. You could create on your own or copy a snippet from the codepen above, but this time I used this library (hex-to-css-filter) to make it easier to implement.

There are two things that you have to be careful.

  1. This library assumes the base color as #000 so if you're using white-based icons, you'll have to replace them with fill #000.
  2. If you are using React inline styling, you have to remove the semi-colon at the end of the value.

Creating React component with SVG

First, create a react component with SVG that accepts props and passes it to SVG element.

import * as React from "react";

const SvgMicOff = (props) => (
  <svg
    width="24"
    height="25"
    viewBox="0 0 24 25"
    fill="none"
    xmlns="http://www.w3.org/2000/svg"
    {...props}
  >
    <path
      d="M1 2L23 24"
      stroke="black"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    />
...
  </svg>
);

export default SvgMicOff;

Enter fullscreen mode Exit fullscreen mode

In App.js, require the component and hex-to-css-filter library.

import MicOff from "../icons/mic-off";
import { hexToCSSFilter } from "hex-to-css-filter";
Enter fullscreen mode Exit fullscreen mode

Setting hex color

In the App component, create a styling passing hexColor dynamically from the state. (and make sure to remove the semi-colon!)

export default function App() {
  const [hexColor, setHexColor] = useState("#000");

  let cssFilterValue = "";
  const cssFilter = hexToCSSFilter(hexColor, {
    acceptanceLossPercentage: 1,
    maxChecks: 10
  });
  cssFilterValue = cssFilter.filter.replace(";", "");
  //semi-colon should be removed from the string
Enter fullscreen mode Exit fullscreen mode

Setting shadow

If you also want to add shadow, you could also create like so

const [shadow, setShadow] = useState("");
...
 const shadowColor =
    shadow === "dark"
      ? "drop-shadow(1px 1px 1px rgba(0,0,0,0.5))"
      : shadow === "light"
      ? "drop-shadow(1px 1px 1px rgba(255,255,255,0.5))"
      : "";
Enter fullscreen mode Exit fullscreen mode

and at last, all you have to do is to connect them as a string :)

<MicOff style={{ filter: `${cssFilterValue} ${shadowColor}` }} />
Enter fullscreen mode Exit fullscreen mode

And this is the result!
<iframe src="https://codesandbox.io/embed/svg-icon-color-with-hex-to-css-filter-mr19p" style="width:100%; height:calc(300px + 8vw); border:0; border-radius: 4px; overflow:hidden;" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" loading="lazy" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"> </iframe>

Feel free to play around, and hope this helps your development :)
Also, please let me know if you find something I can improve!

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐