diff --git a/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx b/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx
new file mode 100644
index 00000000000..bc871085f48
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/DashboardHeader.test.tsx
@@ -0,0 +1,57 @@
+import { afterEach, describe, expect, it, vi } from "vitest";
+import { act, fireEvent, render, screen, waitFor } from "@testing-library/react";
+import { DashboardHeader } from "./DashboardHeader";
+
+const { mockUsePluginMode, mockUseUISettings, state } = vi.hoisted(() => {
+ const state = {
+ plugins: [] as { name: string; display_name: string; url: string }[],
+ enableChatUI: false,
+ };
+ return {
+ state,
+ mockUsePluginMode: vi.fn(() => ({ mode: "ai-gateway", setMode: vi.fn(), plugins: state.plugins })),
+ mockUseUISettings: vi.fn(() => ({ data: { values: { enable_chat_ui: state.enableChatUI } } })),
+ };
+});
+
+vi.mock("@/contexts/PluginModeContext", () => ({ usePluginMode: mockUsePluginMode }));
+vi.mock("@/app/(dashboard)/hooks/uiSettings/useUISettings", () => ({ useUISettings: mockUseUISettings }));
+vi.mock("next/navigation", () => ({ usePathname: () => "/ui/" }));
+vi.mock("@/utils/migratedPages", () => ({ migratedHref: (seg: string) => `/ui/${seg}` }));
+vi.mock("@/hooks/useWorker", () => ({ useWorker: () => ({ isControlPlane: false, selectedWorker: null }) }));
+vi.mock("@/app/(dashboard)/hooks/useDisableShowPrompts", () => ({ useDisableShowPrompts: () => false }));
+vi.mock("@/components/Navbar/BlogDropdown/BlogDropdown", () => ({ BlogDropdown: () => null }));
+vi.mock("@/components/Navbar/CommunityEngagementButtons/CommunityEngagementButtons", () => ({
+ CommunityEngagementButtons: () => null,
+}));
+vi.mock("@/components/Navbar/NotificationsBell/NotificationsBell", () => ({ NotificationsBell: () => null }));
+vi.mock("@/components/Navbar/WorkerDropdown/WorkerDropdown", () => ({ default: () => null }));
+
+describe("DashboardHeader breadcrumb", () => {
+ afterEach(() => {
+ state.plugins = [];
+ state.enableChatUI = false;
+ });
+
+ it("roots the breadcrumb in the AI Gateway selector (with a Chat option) and drops the static section crumb when the selector is available", async () => {
+ state.enableChatUI = true;
+ render();
+
+ expect(screen.getByText("Logs")).toBeInTheDocument();
+ expect(screen.queryByText("Observability")).not.toBeInTheDocument();
+
+ const selector = screen.getByRole("button", { name: /AI Gateway/i });
+ act(() => {
+ fireEvent.click(selector);
+ });
+ await waitFor(() => expect(screen.getByText("Chat")).toBeInTheDocument());
+ });
+
+ it("falls back to the static section crumb when the selector has nothing to switch to", () => {
+ render();
+
+ expect(screen.getByText("Observability")).toBeInTheDocument();
+ expect(screen.getByText("Logs")).toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: /AI Gateway/i })).not.toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/DashboardHeader.tsx b/ui/litellm-dashboard/src/components/DashboardHeader.tsx
index 4f285cefed9..a8537980dbf 100644
--- a/ui/litellm-dashboard/src/components/DashboardHeader.tsx
+++ b/ui/litellm-dashboard/src/components/DashboardHeader.tsx
@@ -13,7 +13,7 @@ import { getBreadcrumb } from "@/components/leftnav";
import { BlogDropdown } from "@/components/Navbar/BlogDropdown/BlogDropdown";
import { CommunityEngagementButtons } from "@/components/Navbar/CommunityEngagementButtons/CommunityEngagementButtons";
import { NotificationsBell } from "@/components/Navbar/NotificationsBell/NotificationsBell";
-import ViewSwitcher from "@/components/Navbar/ViewSwitcher";
+import ViewSwitcher, { useViewSwitcherVisible } from "@/components/Navbar/ViewSwitcher";
import WorkerDropdown from "@/components/Navbar/WorkerDropdown/WorkerDropdown";
import { useWorker } from "@/hooks/useWorker";
import { useDisableShowPrompts } from "@/app/(dashboard)/hooks/useDisableShowPrompts";
@@ -28,6 +28,7 @@ interface DashboardHeaderProps {
// lives in the sidebar header); mirrors the design's breadcrumb-left / tools-right layout.
export function DashboardHeader({ page }: DashboardHeaderProps) {
const { section, title } = getBreadcrumb(page);
+ const showSwitcher = useViewSwitcherVisible();
const { isControlPlane, selectedWorker } = useWorker();
const showWorkerSwitch = isControlPlane && selectedWorker !== null;
const hideCommunityLinks = useDisableShowPrompts();
@@ -44,12 +45,14 @@ export function DashboardHeader({ page }: DashboardHeaderProps) {
- {section && (
- <>
- {section}
-
- >
+ {showSwitcher ? (
+
+
+
+ ) : (
+ section && {section}
)}
+ {(showSwitcher || section) && }
{title}
@@ -76,8 +79,6 @@ export function DashboardHeader({ page }: DashboardHeaderProps) {
{!hideCommunityLinks && }
-
-
);
diff --git a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx
index 834966aca35..b4876f791eb 100644
--- a/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx
+++ b/ui/litellm-dashboard/src/components/Navbar/ViewSwitcher.tsx
@@ -1,7 +1,8 @@
import React from "react";
import { usePathname } from "next/navigation";
import { Dropdown } from "antd";
-import { AppstoreOutlined, CheckOutlined, DownOutlined } from "@ant-design/icons";
+import { AppstoreOutlined, CheckOutlined } from "@ant-design/icons";
+import { ChevronsUpDown } from "lucide-react";
import type { MenuProps } from "antd";
import { usePluginMode } from "@/contexts/PluginModeContext";
import { useUISettings } from "@/app/(dashboard)/hooks/uiSettings/useUISettings";
@@ -10,6 +11,12 @@ import { migratedHref } from "@/utils/migratedPages";
const GATEWAY = "ai-gateway";
const CHAT = "chat";
+export function useViewSwitcherVisible(): boolean {
+ const { plugins } = usePluginMode();
+ const { data: uiSettings } = useUISettings();
+ return plugins.length > 0 || Boolean(uiSettings?.values?.enable_chat_ui);
+}
+
export default function ViewSwitcher() {
const { mode, setMode, plugins } = usePluginMode();
const { data: uiSettings } = useUISettings();
@@ -72,11 +79,13 @@ export default function ViewSwitcher() {
);