useFavicon()
Live demo: useFavicon
Swap the tab icon dynamically using preset and custom favicon URLs.
Loading demo...
Demo sourceJSX
Overview
useFavicon keeps the current favicon path in React state and, on every change, finds or creates a <link rel="…"> in the document head, sets its href to baseUrl + filename, and sets type from the file extension so the browser swaps the tab icon without a full reload. You can point at static assets under a CDN path (baseUrl), match an existing link via rel (for example icon vs shortcut icon), or pass a custom doc when updating a non-default document (tests, embedded HTML). The hook returns a tuple [favicon, setFavicon] like useState, so you can seed an initial icon from newIcon and drive updates from UI or side effects.
What it accepts
- Optional
newIcon- Initial filename or path fragment (joined withbaseUrl). Default''. - Optional
baseUrl- Prefix for the finalhref(for example/icons/or a CDN origin). Default''. - Optional
rel-relselector / attribute for the link node (querySelector('link[rel*="…"]')). Default'icon'. - Optional
doc- TargetDocument(defaultdocument).
What it returns
- A tuple
[favicon, setFavicon]: current icon string and a state setter that triggers a head<link>update on the next commit.
Usage
Example: default vs “busy” favicon under a shared asset prefix, with an explicit rel and document (all options visible).
import useFavicon from '@dedalik/use-react/useFavicon'
function Example() {
const [favicon, setFavicon] = useFavicon({
newIcon: 'favicon.ico',
baseUrl: '/brand/icons/',
rel: 'icon',
doc: document,
})
return (
<div>
<h3>Tab icon</h3>
<p>
Current file: <strong>{favicon || '-'}</strong>
</p>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<button type='button' onClick={() => setFavicon('favicon.ico')}>
Default
</button>
<button type='button' onClick={() => setFavicon('favicon-busy.png')}>
Busy
</button>
</div>
<p style={{ marginTop: 12, opacity: 0.75 }}>
The tab icon updates without a full reload; in devtools → Elements → head, the matching link href becomes{' '}
<code>/brand/icons/</code> plus the filename.
</p>
</div>
)
}
export default function Demo() {
return <Example />
}API Reference
useFavicon
Signature: useFavicon(options?: UseFaviconOptions): UseFaviconReturnType
Parameters
Optional object UseFaviconOptions:
newIcon(string, optional) - Initial icon filename or suffix; combined withbaseUrlforhref. Default''.baseUrl(string, optional) - Prefix prepended tonewIcon. Default''.rel(string, optional) - Used to find/create the<link>(link[rel*="…"]). Default'icon'.doc(Document, optional) - Document whose<head>is updated. Defaultdocument.
Returns
Tuple [favicon, setFavicon] (UseFaviconReturnType):
favicon- Current icon string in state.setFavicon- React state setter; updates state and re-runs the effect that applies the icon.
Copy-paste hook
TypeScript
import React, { useState, useEffect, useCallback } from 'react'
export interface UseFaviconOptions {
newIcon?: string
baseUrl?: string
rel?: string
doc?: Document
}
export type UseFaviconReturnType = [string, React.Dispatch<React.SetStateAction<string>>]
const useFavicon = ({
newIcon = '',
baseUrl = '',
rel = 'icon',
doc = document,
}: UseFaviconOptions = {}): UseFaviconReturnType => {
const [favicon, setFavicon] = useState<string>(newIcon)
const applyIcon = useCallback(
(icon: string) => {
let linkElement = doc.head.querySelector(`link[rel*="${rel}"]`) as HTMLLinkElement
if (!linkElement) {
linkElement = doc.createElement('link')
linkElement.rel = rel
doc.head.appendChild(linkElement)
}
const iconUrl = `${baseUrl}${icon}`
if (linkElement.href !== iconUrl) {
linkElement.href = iconUrl
linkElement.type = `image/${icon.split('.').pop()}`
}
},
[baseUrl, rel, doc],
)
useEffect(() => {
if (typeof favicon === 'string') {
applyIcon(favicon)
}
}, [favicon, applyIcon])
return [favicon, setFavicon]
}
export default useFavicon
export type UseFaviconType = ReturnType<typeof useFavicon>JavaScript
import React, { useState, useEffect, useCallback } from 'react'
const useFavicon = ({ newIcon = '', baseUrl = '', rel = 'icon', doc = document } = {}) => {
const [favicon, setFavicon] = useState(newIcon)
const applyIcon = useCallback(
(icon) => {
let linkElement = doc.head.querySelector(`link[rel*="${rel}"]`)
if (!linkElement) {
linkElement = doc.createElement('link')
linkElement.rel = rel
doc.head.appendChild(linkElement)
}
const iconUrl = `${baseUrl}${icon}`
if (linkElement.href !== iconUrl) {
linkElement.href = iconUrl
linkElement.type = `image/${icon.split('.').pop()}`
}
},
[baseUrl, rel, doc],
)
useEffect(() => {
if (typeof favicon === 'string') {
applyIcon(favicon)
}
}, [favicon, applyIcon])
return [favicon, setFavicon]
}
export default useFavicon