useSuspenseFavicon
Loads a favicon and suspends the component until the resource is ready.
About
A Suspense-enabled hook for reading and updating the current document favicon. It resolves the current explicit favicon from the DOM, classifies it as same-origin or external, and returns controls for switching to a new favicon using an explicit config object.
If the document does not declare a favicon link, the current value is null.
Examples
Basic Usage
import React, { Suspense } from "react";
import { useSuspenseFavicon } from "rooks/experimental";
function FaviconPreview() {
const [favicon, { updateFaviconURL }] = useSuspenseFavicon();
return (
<div>
<pre>{JSON.stringify(favicon, null, 2)}</pre>
<button
onClick={() =>
updateFaviconURL({
kind: "same-origin",
relativeHref: "/favicon-light.svg?v=2",
})
}
>
Use same-origin favicon
</button>
<button
onClick={() =>
updateFaviconURL({
kind: "external",
url: "https://cdn.example.com/brand/favicon-dark.ico",
})
}
>
Use external favicon
</button>
</div>
);
}
export default function App() {
return (
<Suspense fallback={<div>Reading favicon...</div>}>
<FaviconPreview />
</Suspense>
);
}Same-origin Updates With Cache-busting Query Strings
import React, { Suspense } from "react";
import { useSuspenseFavicon } from "rooks/experimental";
function BrandSwitcher() {
const [favicon, { updateFaviconURL }] = useSuspenseFavicon();
return (
<div>
<p>Current kind: {favicon?.kind ?? "none"}</p>
<button
onClick={() =>
updateFaviconURL({
kind: "same-origin",
relativeHref: "/favicons/workspace-a.svg?v=12",
})
}
>
Workspace A
</button>
<button
onClick={() =>
updateFaviconURL({
kind: "same-origin",
relativeHref: "../favicons/workspace-b.svg?v=3#active",
})
}
>
Workspace B
</button>
</div>
);
}
export default function App() {
return (
<Suspense fallback={<div>Loading favicon state...</div>}>
<BrandSwitcher />
</Suspense>
);
}Persisting The Managed Favicon After Unmount
import React, { Suspense, useState } from "react";
import { useSuspenseFavicon } from "rooks/experimental";
function ManagedFavicon() {
const [, { updateFaviconURL }] = useSuspenseFavicon({
unmountStrategy: "leave-as-is",
});
return (
<button
onClick={() =>
updateFaviconURL({
kind: "external",
url: "https://cdn.example.com/persistent/favicon.ico",
})
}
>
Apply persistent favicon
</button>
);
}
export default function App() {
const [showManager, setShowManager] = useState(true);
return (
<Suspense fallback={<div>Preparing favicon manager...</div>}>
<button onClick={() => setShowManager((current) => !current)}>
Toggle hook
</button>
{showManager ? <ManagedFavicon /> : null}
</Suspense>
);
}Parameters
options
| Property | Type | Default | Description |
|---|---|---|---|
unmountStrategy | "restore-originals" | "leave-as-is" | "restore-originals" | Controls what happens when the last mounted useSuspenseFavicon instance unmounts. |
Return value
The hook returns a tuple with two items:
[
currentFavicon,
{
updateFaviconURL,
},
]currentFavicon
| Shape | Description |
|---|---|
null | No explicit favicon link was found during initialization. |
{ kind: "same-origin", relativeHref, href } | The current favicon resolves to the same origin as the current page. relativeHref is the same-origin value you can reuse with updateFaviconURL. |
{ kind: "external", url, href } | The current favicon resolves to a different origin. |
controls.updateFaviconURL
updateFaviconURL(
| { kind: "same-origin"; relativeHref: string }
| { kind: "external"; url: string }
): voidConfig Rules
same-originupdates must use a relative href string such as/favicon.svg?v=2or../icons/favicon.svg.externalupdates must resolve to a different origin than the current page.- Invalid configs throw synchronously with a descriptive error.
Notes
- The hook suspends only while it discovers the current explicit favicon from the DOM.
- When multiple favicon links are present, the last explicit
rel~="icon"link in DOM order is treated as current. - The hook does not observe arbitrary third-party favicon mutations after mount. It tracks the initial DOM state plus updates made through
updateFaviconURL.
Behavior and lifecycle
Initial discovery suspends once while the shared resource finds the last valid <link rel="icon">. The first update snapshots and removes the original icon links, then all mounted instances share one managed link and receive subsequent updates. After the last instance unmounts, "restore-originals" removes the managed link and restores the snapshot; "leave-as-is" keeps the current DOM link and discards the snapshot.
Compatibility and accessibility
The hook throws outside a browser or when document.head is unavailable, so place it behind a client boundary as well as Suspense and an error boundary. Same-origin updates reject empty, absolute, protocol-relative, or cross-origin values; external updates reject empty or same-origin URLs. A favicon is decorative and must not be the only indication of unread, error, or status state.