Skip to content

feat(ui): let admins supply a dark-mode variant of their custom logo - #37662

Merged
yuneng-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_/dark-mode-logo-8fa590
Aug 20, 2026
Merged

feat(ui): let admins supply a dark-mode variant of their custom logo#37662
yuneng-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_/dark-mode-logo-8fa590

Conversation

@yuneng-berri

@yuneng-berri yuneng-berri commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • A custom logo shows its light artwork on a dark sidebar
  • Admins had no way to supply a dark version

How it solves it:

  • Adds UI_LOGO_PATH_DARK and a logo_url_dark theme setting
  • Dark mode falls back to the light logo when it is unset
  • A broken dark logo falls back too, never to LiteLLM's

User Flow

Before: an admin who branded the dashboard with their own logo sees that light-background logo on the dark sidebar, and there is nothing they can set to fix it

  1. They set UI_LOGO_PATH to their company logo and restart the proxy
  2. They open http://localhost:4000/ui/ with dark styling applied and see their light logo on the dark sidebar
  3. They look through http://localhost:4000/ui/?page=ui-theme and find only one logo field, so there is no dark option to set

After: the same admin can supply a dark logo, and doing nothing still keeps their branding

  1. They open http://localhost:4000/ui/?page=ui-theme and see a second field, "Custom Logo URL (dark mode)", which says it reuses the logo above when left empty
  2. They leave it empty, and dark mode keeps showing their own light logo rather than LiteLLM's
  3. They fill it in with a dark-background version and save
  4. Dark mode now shows that logo, and light mode still shows the original
  5. If the dark logo later 404s or is deleted, dark mode drops back to their light logo instead of reverting to LiteLLM branding

Relevant issues

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • The handful of test files covering my change pass locally, e.g. uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*, make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Shared setup: two stand-in customer logos, deliberately different shapes so the served bytes identify themselves, and a proxy on http://localhost:4001 restarted for each case

customer-light.png: PNG image data, 40 x 10, 8-bit/color RGB, non-interlaced
customer-dark.png:  PNG image data, 60 x 20, 8-bit/color RGBA, non-interlaced

Before (edbb342)

Only UI_LOGO_PATH set, asking for the dark logo

  1. UI_LOGO_PATH=customer-light.png, no dark var exists to set
  2. curl -s -o dark.out 'http://localhost:4001/get_image?theme=dark'; file dark.out
dark.out: PNG image data, 40 x 10, 8-bit/color RGB, non-interlaced

The light artwork, which is the only outcome reachable here: grep -c UI_LOGO_PATH_DARK litellm/proxy/proxy_server.py returns 0 at this commit

Both logos set

  1. There is no UI_LOGO_PATH_DARK, so this case cannot be expressed

A broken dark logo

  1. There is no UI_LOGO_PATH_DARK, so this case cannot be expressed

After (dab098b)

Only UI_LOGO_PATH set, asking for the dark logo

  1. curl -s -o light.out http://localhost:4001/get_image and curl -s -o dark.out 'http://localhost:4001/get_image?theme=dark'
light: HTTP 200 93B
dark:  HTTP 200 93B
  1. cmp light.out dark.out exits silently, so an admin who never sets a dark logo keeps their own logo in dark mode

This case is deliberately identical to Before. The fallback already existed, and the point here is that adding the dark option does not disturb it

Both logos set

  1. UI_LOGO_PATH=customer-light.png UI_LOGO_PATH_DARK=customer-dark.png, then the same two requests
light: HTTP 200 93B
dark:  HTTP 200 119B
  1. file -b light.out dark.out
PNG image data, 40 x 10, 8-bit/color RGB, non-interlaced
PNG image data, 60 x 20, 8-bit/color RGBA, non-interlaced

A broken dark logo

  1. UI_LOGO_PATH_DARK=does-not-exist.png with the light logo still valid
  2. curl -s -o dark.out 'http://localhost:4001/get_image?theme=dark'; file -b dark.out
dark:  HTTP 200 93B
PNG image data, 40 x 10, 8-bit/color RGB, non-interlaced
  1. The proxy log says why, and the admin keeps their own branding rather than reverting to LiteLLM's
2026-08-20 12:11:24 - LiteLLM Proxy - WARNING - proxy_server.py:15271 - _serve_custom_ui_logo() - Custom UI logo '.../does-not-exist.png' is not a supported image file or does not exist, falling back

No custom logo at all

  1. With both vars unset, cmp against the bundled files passes on each theme
light: HTTP 200 24694B
dark:  HTTP 200 35771B
light == bundled logo.jpg
dark  == bundled logo_dark.png

Type

🆕 New Feature

Caveats (if any)

  • A dark logo alone leaves light mode on the bundled default
  • Dark mode toggle still not shipped, so this lands ahead of it
  • logo_url_dark accepts http(s) URLs only, like logo_url

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

A deployment branded through UI_LOGO_PATH got its light artwork on the
dark sidebar, and there was nothing an admin could set to change that.

Adds UI_LOGO_PATH_DARK, exposed as the logo_url_dark theme setting and a
second field on the UI theme page. /get_image now walks an ordered list
of candidates for the requested theme and serves the first usable one:
the dark logo, then the light logo, then the bundled default.

Falling through rather than failing is the point. An admin who never
sets a dark logo keeps their own light one instead of reverting to
LiteLLM's, and a dark logo that goes missing later degrades to their
light logo rather than dropping their branding entirely.
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds separately configurable dark-mode branding while retaining the existing light-logo and bundled-logo fallback behavior.

  • Adds UI_LOGO_PATH_DARK and persists it through the UI theme settings API.
  • Exposes the dark-logo setting in the dashboard theme editor and shared theme context.
  • Selects the dark logo in the sidebar and falls back to the light logo after a load error.
  • Adds backend and frontend coverage for selection, persistence, reset, and fallback behavior.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
litellm/proxy/proxy_server.py Adds validated dark-logo candidate selection while preserving light and bundled fallback behavior.
litellm/proxy/ui_crud_endpoints/proxy_setting_endpoints.py Adds the dark-logo theme field and keeps its process and persisted environment values synchronized.
ui/litellm-dashboard/src/app/(dashboard)/ui-theme/UIThemeSettings.tsx Adds editing, saving, and resetting support for the dark-mode logo URL.
ui/litellm-dashboard/src/contexts/ThemeContext.tsx Makes the configured dark-logo URL available through shared dashboard theme state.
ui/litellm-dashboard/src/components/leftnav.tsx Selects the configured dark logo and switches to the light-logo fallback when loading fails.

Reviews (2): Last reviewed commit: "fix(ui): recover from a dark logo the br..." | Re-trigger Greptile

Comment thread ui/litellm-dashboard/src/components/leftnav.tsx Outdated
@codecov

codecov Bot commented Aug 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

A dark logo given as an http(s) URL is loaded by the browser straight
from the sidebar, so it never passes through the proxy's fallback chain.
A URL that 404s left a broken image where the admin's light logo should
have been, while the same logo given as a local path fell back cleanly.

The sidebar now remembers the dark URL that failed and drops to the light
logo, matching how the proxy resolves an unusable dark logo and how the
provider Logo component already handles a broken image.
@yuneng-berri

Copy link
Copy Markdown
Contributor Author

Fixed: the sidebar now remembers a dark logo URL that fails to load and falls back to the light logo. @greptileai

@codspeed-hq

codspeed-hq Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_/dark-mode-logo-8fa590 (dab098b) with litellm_internal_staging (d542c82)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (4873567) during the generation of this report, so d542c82 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@yuneng-berri
yuneng-berri enabled auto-merge (squash) August 20, 2026 19:46

@tin-berri tin-berri left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Small, well-scoped feature — new UI_LOGO_PATH_DARK env var / logo_url_dark theme setting, with sensible fallback chain (no dark logo set -> keep light logo in dark mode; broken dark logo -> also falls back to the light logo, never reverts to LiteLLM's bundled branding, which is the right call for a white-labeled proxy). Proof-of-fix is concrete: real served-byte comparisons (cmp/file) across all 4 cases (unset, both set, broken dark, both unset falling back to bundled defaults) rather than just describing behavior. No auth/billing/security surface — pure static asset serving. CI fully green (78 checks).

@yuneng-berri
yuneng-berri merged commit 122675c into litellm_internal_staging Aug 20, 2026
72 of 74 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_/dark-mode-logo-8fa590 branch August 20, 2026 19:48
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.

2 participants