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.
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.
- 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.
- 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;
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";
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
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))"
: "";
and at last, all you have to do is to connect them as a string :)
<MicOff style={{ filter: `${cssFilterValue} ${shadowColor}` }} />
And this is the result!
Feel free to play around, and hope this helps your development :)
Also, please let me know if you find something I can improve!
Top comments (3)
In our projects we replace stroke and fill values with
currentColor
. This way we can change icon color with just one line of css.Thanks for your reply Denis! Yes, I think for simple icons, your approach would be a better one. The reason why I tried this way was that I found for some icons that are more complex and mixing fill/stroke color (e.g. "#000", "rgba(0,0,0,0.5)" or "none"), that approach modified unwanted values. If you ever need to tackle these cases, this approach would work without problems and can be set verbose.
Thank you 👏👏👏
Some comments may only be visible to logged-in visitors. Sign in to view all comments.