Skip to content

Multicolored icons

The recommended solution is to use CSS variables in SVG images. This way, we won’t need to replace whole images set when only one color in the palette changes.

This method allows designers and developers to work seamlessly and share same icon sets without any additional manual modifications.

vite-awesome-svg-loader allows you to map a set of predefined colors (or any values for that matter) to a set of CSS variables (or any other values) via replaceColorsList option:

vite.config.ts
export default defineConfig({
plugins: [
viteAwesomeSvgLoader({
replaceColorsList:
// Global map of color replacements. Key is an original color, value is its replacement. Both can be any values:
// HEX, name, rgb() or arbitrary custom values. Applied to all files.
{
"#003147": "red",
"rgb(0, 49, 71)": "#003147",
"myCustomColor": "var(--some-color-var)",
},
// Map of color replacements per files
{
files: ["vars.svg"], // File names or regexes
// Replacements, same format as above
replacements: {
red: "var(--primary-color)",
green: "var(--secondary-color)",
blue: "var(--tertiary-color)",
},
// Default value for colors that are not in replacements map. Set an empty string to preserve original colors.
// Default value is "currentColor",
default: "currentColor"
},
}),
],
});
Rendered demo
Demo's source code
src
assets
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 24 24"
xml:space="preserve"
>
<circle
fill="red"
cx="8.0524569"
cy="8.1588478"
r="7.8950858"
/>
<circle
fill="blue"
cx="15.9475431"
cy="8.1588478"
r="7.8950858"
/>
<circle
fill="green"
cx="12"
cy="15.8411522"
r="7.8950858"
/>
<style type="text/css">
circle {
opacity: 0.5;
}
</style>
</svg>
import imageSrc from "@/assets/image.svg";
import { CSSProperties } from "react";
import { SvgImage } from "vite-awesome-svg-loader/react-integration";
export default function App() {
return (
<div className="images">
<SvgImage
src={imageSrc}
className="image"
style={
{
"--primary-color": "red",
"--secondary-color": "green",
"--tertiary-color": "blue",
} as CSSProperties
}
/>
<SvgImage
src={imageSrc}
className="image"
style={
{
"--primary-color": "magenta",
"--secondary-color": "cyan",
"--tertiary-color": "yellow",
} as CSSProperties
}
/>
</div>
);
}
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
ReactDOM.createRoot(document.getElementById("app")!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
/// <reference types="vite/client" />
// Link some of the import configs. Unfortunately, it's impossible to fully type it. Use plugin configuration instead.
// Use import config when you really need it to avoid @ts-ignore before every import.
/// <reference types="vite-awesome-svg-loader" />
// Internal types
/// <reference types="vite-file-tree-builder" />
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1"
/>
</head>
<body>
<div id="app"></div>
<script
type="module"
src="/src/main.tsx"
></script>
</body>
</html>
{
"name": "react-multicolored-icons",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "npm run build -- --watch",
"build": "npm run type-check && npm run build-only",
"build-only": "vite build --config vite.config.lib.ts",
"type-check": "tsc --noEmit",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
},
"dependencies": {
"vite-awesome-svg-loader": "*",
"vite-file-tree-builder": "*"
}
}
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }],
}
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// Import vite-awesome-svg-loader
import { viteAwesomeSvgLoader } from "vite-awesome-svg-loader";
export default defineConfig({
plugins: [
react(),
viteAwesomeSvgLoader({
replaceColorsList: [
{
files: ["image.svg"], // File names or regexes
// Replacements, same format as above
replacements: {
red: "var(--primary-color)",
green: "var(--secondary-color)",
blue: "var(--tertiary-color)",
},
// Default value for colors that are not in replacements map. Set an empty string to preserve original colors.
// Default value is "currentColor",
default: "currentColor",
},
],
urlImportsInLibraryMode: "emit-files",
}),
],
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
Please open file to view its content