Dynamic Favicon
Strategy Best For Notes
- Hostname/Subdomain One app deployed for all clients Dynamic, one build
- Env Variable per Build Separate builds per client Static favicon per client build
- API-based User-based or late client detection Set favicon after API response
Strategy | Best For | Notes |
---|---|---|
Hostname/Subdomain | One app deployed for all clients | Dynamic, one build |
Env Variable per Build | Separate builds per client | Static favicon per client build |
API-based | User-based or late client detection | User-based or late client detection |
Env Variable METHOD
config.js
export const DOMAIN_LISTS = {
INFOCOM: "info",
PRABHU: "bank",
NIMB: "nimb",
ADBL: "adbl",
};
export const PATH = {
ASSETS: "/assets/",
};
export const FAVICONS = {
[DOMAIN_LISTS.INFOCOM]: `${PATH.ASSETS}infocom.ico`,
[DOMAIN_LISTS.PRABHU]: `${PATH.ASSETS}prabhu.ico`,
[DOMAIN_LISTS.NIMB]: `${PATH.ASSETS}nimb.ico`,
[DOMAIN_LISTS.ADBL]: `${PATH.ASSETS}adbl.ico`,
};
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- REMOVE THIS FROM HERE -->
<!-- <link rel="icon" type="image/svg+xml" href="/assets/favicon.ico" /> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bank App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
App.jsx
import { DOMAIN_LISTS, FAVICONS, PATH } from "@shared/utils/config"; // Adjust the import path as needed
const buildMode = import.meta.env.VITE_REACT_APP_BUILD_MODE; // e.g. "adbl", "info", etc.
import { useEffect } from "react";
import { DOMAIN_LISTS, FAVICONS, PATH } from "@shared/utils/config";
function App() {
const buildMode = import.meta.env.VITE_REACT_APP_BUILD_MODE;
useEffect(() => {
let favicon = document.querySelector("link[rel~='icon']");
if (!favicon) {
favicon = document.createElement("link");
favicon.rel = "icon";
favicon.type = "image/x-icon";
document.head.appendChild(favicon);
}
// Set favicon based on buildMode, fallback to default.ico if not found
favicon.href = FAVICONS[buildMode] || `${PATH.ASSETS}default.ico`;
}, [buildMode]);
return (
// Your component JSX here
);
}
export default App;
What this does:
On component mount or whenever buildMode
changes, it:
- Looks for existing
<link rel="icon">
tag or creates one. - Sets its
href
to the correct favicon path from yourFAVICONS
map. - Falls back to
/assets/default.ico
ifbuildMode
is not mapped.
Bonus tips:
- Make sure
default.ico
exists at/assets/default.ico
. - Ensure your favicon files exist at the correct locations.
- Clear browser cache or test in incognito to see changes immediately.
Add a small cache-busting query param to avoid favicon being stuck in browser cache (optional):
Only necessary during dev or frequent icon switching, though.
Include a fallback <link rel="icon" />
in index.html
for early loading
This prevents the browser from defaulting to /favicon.ico
before your React app mounts.
In public/index.html
(or whatever your entry HTML is):
Then your dynamic logic will override this as soon as React loads.
EXTRA
If i add the the
favicon.ico
in theindex.html
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- THIS -->
<link rel="icon" type="image/svg+xml" href="/assets/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bank App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
useEffect(() => {
// Remove any existing favicon tags
const existingIcons = document.querySelectorAll("link[rel*='icon']");
existingIcons.forEach((el) => el.parentNode.removeChild(el));
const favicon = document.createElement("link");
favicon.rel = "icon";
favicon.type = "image/x-icon";
favicon.href = `${
FAVICONS[CONFIG.BUILD_MODE] || `${PATH.ASSETS}default.ico`
}?v=${Date.now()}`;
document.head.appendChild(favicon);
}, [CONFIG.BUILD_MODE]);
Antd
import React from "react";
import { Image } from "antd";
const App = () => (
<Image
width={200}
height={200}
src="error"
fallback="data:image/png;base64,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"
/>
);
export default App;