Skip to content

fix: favicon badge — remove favicon.ico, fix Firefox compat#66

Open
jabiinfante wants to merge 1 commit intoroot-fr:mainfrom
nykk-io:fix/favicon-badge-firefox-compat
Open

fix: favicon badge — remove favicon.ico, fix Firefox compat#66
jabiinfante wants to merge 1 commit intoroot-fr:mainfrom
nykk-io:fix/favicon-badge-firefox-compat

Conversation

@jabiinfante
Copy link
Copy Markdown

Problem

The favicon badge was broken in Firefox and Chrome on Linux.

The original implementation appended a second <link rel="icon"> to the
<head> and left the Next.js-rendered one intact. This caused two issues:

  1. First-vs-last selection: Chrome on Windows picks the last matching
    <link rel="icon">; Firefox and Chrome on Linux pick the first. With
    two links in the DOM, the badge link (appended last) was systematically
    ignored on Linux.

  2. Firefox ignores dynamically added links: Firefox caches the favicon
    from the element it tracked at page load. Inserting a new <link> node
    at runtime is silently ignored — Firefox only reacts when the href of
    the existing element changes.

Solution

Remove app/favicon.ico so Next.js emits a single <link rel="icon">
pointing to icon.svg. One link = no browser disagreement.

Mutate href on that link directly instead of injecting a second one.
On mount the original href is saved; the badge overwrites it on each
count change; cleanup restores it on unmount. This satisfies Firefox and
is React-safe: the reconciler crash (parentNode is null) only happens
when a tracked DOM node is removed, not when its attributes are mutated.

Other fixes

  • Simplified image loading: fetch → blob → FileReader → Image replaced
    with new Image() + crossOrigin="anonymous" (same result, less code).
  • Fixed badge border rendering in the Safari ≤15 fallback: stroke()
    after fillRect() was a no-op because fillRect does not write to the
    canvas path. Changed to strokeRect().

- Delete app/favicon.ico so Next.js renders a single <link rel="icon">
  for icon.svg. With two icon links in the DOM, browsers disagreed on
  which to use: Chrome and Firefox on Linux both picked the *first* one,
  making the dynamically-appended badge link invisible.
- Mutate href on the existing <link> instead of injecting a second one.
  Firefox only updates the tab icon when the href of the element it
  already tracks changes — adding new <link> nodes dynamically is ignored.
- Simplify image loading: replace fetch → blob → FileReader → Image
  chain with new Image() + crossOrigin="anonymous".
- Fix badge border in Safari 15 fallback: use strokeRect instead of
  stroke() after fillRect (fillRect does not add to the canvas path).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant