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>,
);
<!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/*"]
},
"types": [
// Add types for query imports (for example, "@/some/file.svg?src").
// Unfortunately, it's impossible to fully type it. Use plugin configuration instead.
"vite-awesome-svg-loader",
"vite/client", // Vite types
"vite-file-tree-builder" // Internal types
]
},
"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