Skip to content

fix(favicon): update favicon dynamically based on app theme#4118

Open
mixelburg wants to merge 1 commit intoDokploy:canaryfrom
mixelburg:fix/theme-aware-favicon
Open

fix(favicon): update favicon dynamically based on app theme#4118
mixelburg wants to merge 1 commit intoDokploy:canaryfrom
mixelburg:fix/theme-aware-favicon

Conversation

@mixelburg
Copy link
Copy Markdown
Contributor

@mixelburg mixelburg commented Mar 31, 2026

Fixes #4100

The favicon was only responding to the OS-level color scheme via CSS @media (prefers-color-scheme: dark), not Dokploy's class-based theme (via next-themes). Toggling the app theme had no effect on the favicon until a page reload.

Root cause: The favicon is an external resource (not in the DOM), so it cannot observe <html class="dark"> changes — it only responds to the OS media query.

Fix:

  1. Added icon-light.svg and icon-dark.svg to /public — static variants with explicit fill="black" and fill="white" respectively, with the <style> block removed.

  2. Extended WhitelabelingProvider to inject a <link rel="icon"> via next/head that reflects the current app theme (resolvedTheme from useTheme()):

    • Custom faviconUrl from white-label config takes precedence
    • Falls back to /icon-dark.svg in dark mode, /icon-light.svg in light mode
    • Falls back to /icon.svg while theme is resolving
  3. The original /icon.svg in _document.tsx stays as the pre-hydration fallback (gives OS-matched favicon before JS loads).

Greptile Summary

This PR fixes a bug where Dokploy's favicon only responded to the OS-level color scheme (prefers-color-scheme media query) and not to the app-level theme managed by next-themes. Two new static SVG variants are added to /public — one for dark mode (white fill) and one for light mode (black fill) — with the dynamic CSS media query block removed from both.

Key changes:

  • WhitelabelingProvider now always renders a <link rel="icon"> via next/head, driven by resolvedTheme from useTheme(), so the favicon updates immediately whenever the user toggles the app theme.
  • The component no longer returns null while the whitelabeling config is loading, ensuring theme-aware favicon injection works independently of config fetch state.
  • Custom faviconUrl from whitelabeling config continues to take precedence. When resolvedTheme is undefined (SSR / before hydration), the fallback is /icon.svg, matching the pre-hydration favicon already set in _document.tsx.
  • The _document.tsx entry is intentionally preserved as the pre-hydration fallback, and the next/head entry overrides it in the browser after hydration.

Confidence Score: 5/5

Safe to merge — no logic bugs, data integrity issues, or security concerns.

All three changed files are straightforward and correct. The theme-based favicon selection handles all expected states (undefined/SSR, dark, light, custom URL). The pre-hydration fallback in _document.tsx is preserved as intended. No P0 or P1 issues found.

No files require special attention.

Important Files Changed

Filename Overview
apps/dokploy/components/proprietary/whitelabeling/whitelabeling-provider.tsx Correctly integrates useTheme() to dynamically update the favicon based on the resolved app theme; graceful fallbacks for loading states and custom whitelabeling config are all handled.
apps/dokploy/public/icon-dark.svg New dark-mode favicon with explicit white fill and no CSS media query; correct static variant for use after theme resolution.
apps/dokploy/public/icon-light.svg New light-mode favicon with explicit black fill and no CSS media query; correct static variant for use after theme resolution.

Reviews (1): Last reviewed commit: "fix(favicon): update favicon dynamically..." | Re-trigger Greptile

@mixelburg mixelburg requested a review from Siumauricio as a code owner March 31, 2026 22:32
@dosubot dosubot bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Mar 31, 2026
Copy link
Copy Markdown

@jwchmodx jwchmodx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: brief favicon flash for whitelabeled deployments

When config is still loading (undefined), faviconHref correctly falls back to the theme-based SVG. However, if the user has a custom faviconUrl set in whitelabeling config, there will be a short flash of the default theme icon before the config loads and overrides it.

This is probably acceptable — the flash is short and the pre-hydration _document.tsx entry already shows an OS-matched icon. Worth noting in case brand consistency is a concern for whitelabeled deployments (e.g. enterprise customers). If it ever becomes a visible issue, one option is caching the faviconUrl in localStorage so it's available before the query resolves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Favicon does not update on theme toggle without page reload

2 participants