From 519afa494cd76762e1ea2b24e3961d6477c74cdd Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 19 Mar 2026 12:57:17 -0700 Subject: [PATCH 1/4] [Refactor] UI - API Reference: Migrate to path-based routing Move the API Reference page from query-param routing (?page=api_ref) to Next.js path-based routing (/ui/api-reference). Add a LEGACY_REDIRECTS map in the root page.tsx so users with old bookmarks are seamlessly redirected. Future page migrations only need one new map entry. Co-Authored-By: Claude Opus 4.6 --- .../app/(dashboard)/api-reference/page.tsx | 10 ++------- .../app/(dashboard)/components/Sidebar2.tsx | 2 +- .../hooks/proxySettings/useProxySettings.ts | 20 +++++++++++++++++ ui/litellm-dashboard/src/app/page.tsx | 22 +++++++++++++++---- .../src/components/leftnav.tsx | 4 ++-- .../src/components/page_metadata.ts | 2 +- 6 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts diff --git a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx index f9f26c0eac..02bed1adbe 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/api-reference/page.tsx @@ -1,16 +1,10 @@ "use client"; import APIReferenceView from "@/app/(dashboard)/api-reference/APIReferenceView"; -import { useState } from "react"; - -interface ProxySettings { - PROXY_BASE_URL: string; - PROXY_LOGOUT_URL: string; - LITELLM_UI_API_DOC_BASE_URL?: string | null; -} +import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings"; const APIReferencePage = () => { - const [proxySettings, setProxySettings] = useState({ PROXY_BASE_URL: "", PROXY_LOGOUT_URL: "" }); + const proxySettings = useProxySettings(); return ; }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx b/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx index 18ab475f22..27a6e6c13b 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/components/Sidebar2.tsx @@ -195,7 +195,7 @@ const menuItems: MenuItemCfg[] = [ icon: , roles: all_admin_roles, }, - { key: "14", page: "api_ref", label: "API Reference", icon: }, + { key: "14", page: "api-reference", label: "API Reference", icon: }, { key: "16", page: "model-hub-table", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts new file mode 100644 index 0000000000..62de696b7a --- /dev/null +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts @@ -0,0 +1,20 @@ +import { useState, useEffect } from "react"; +import { fetchProxySettings } from "@/utils/proxyUtils"; +import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; + +export default function useProxySettings() { + const { accessToken } = useAuthorized(); + const [proxySettings, setProxySettings] = useState({ + PROXY_BASE_URL: "", + PROXY_LOGOUT_URL: "", + }); + + useEffect(() => { + if (!accessToken) return; + fetchProxySettings(accessToken).then((settings) => { + if (settings) setProxySettings(settings); + }); + }, [accessToken]); + + return proxySettings; +} diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 5f2921203f..7ee48317e9 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -1,6 +1,5 @@ "use client"; -import APIReferenceView from "@/app/(dashboard)/api-reference/APIReferenceView"; import SidebarProvider from "@/app/(dashboard)/components/SidebarProvider"; import OldModelDashboard from "@/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView"; import PlaygroundPage from "@/app/(dashboard)/playground/page"; @@ -48,7 +47,7 @@ import { buildLoginUrlWithReturn, consumeReturnUrl, normalizeUrlForCompare, stor import { formatUserRole, isAdminRole } from "@/utils/roles"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { jwtDecode } from "jwt-decode"; -import { useSearchParams } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { Suspense, useEffect, useMemo, useRef, useState } from "react"; import { ConfigProvider, theme } from "antd"; @@ -75,6 +74,15 @@ interface ProxySettings { LITELLM_UI_API_DOC_BASE_URL?: string | null; } +/** + * Map of legacy query-param page keys → new path-based route segments. + * When a user visits ?page=, they are redirected to /ui/. + * Add entries here as pages are migrated from the if/else chain to path-based routes. + */ +const LEGACY_REDIRECTS: Record = { + api_ref: "api-reference", +}; + function CreateKeyPageContent() { const [userRole, setUserRole] = useState(""); const [premiumUser, setPremiumUser] = useState(false); @@ -90,6 +98,7 @@ function CreateKeyPageContent() { }); const [showSSOBanner, setShowSSOBanner] = useState(true); + const router = useRouter(); const searchParams = useSearchParams()!; const [modelData, setModelData] = useState({ data: [] }); const [token, setToken] = useState(null); @@ -431,6 +440,13 @@ function CreateKeyPageContent() { return ; } + // Redirect legacy query-param pages to their new path-based routes + if (page in LEGACY_REDIRECTS) { + const base = (proxyBaseUrl || "") + "/ui"; + router.replace(`${base}/${LEGACY_REDIRECTS[page]}`); + return ; + } + return ( }> - ) : page == "api_ref" ? ( - ) : page == "logging-and-alerts" ? ( ) : page == "budgets" ? ( diff --git a/ui/litellm-dashboard/src/components/leftnav.tsx b/ui/litellm-dashboard/src/components/leftnav.tsx index d01fc06bc0..30b3a9653f 100644 --- a/ui/litellm-dashboard/src/components/leftnav.tsx +++ b/ui/litellm-dashboard/src/components/leftnav.tsx @@ -231,8 +231,8 @@ const menuGroups: MenuGroup[] = [ groupLabel: "DEVELOPER TOOLS", items: [ { - key: "api_ref", - page: "api_ref", + key: "api-reference", + page: "api-reference", label: "API Reference", icon: , }, diff --git a/ui/litellm-dashboard/src/components/page_metadata.ts b/ui/litellm-dashboard/src/components/page_metadata.ts index a910373d66..fdfc321f38 100644 --- a/ui/litellm-dashboard/src/components/page_metadata.ts +++ b/ui/litellm-dashboard/src/components/page_metadata.ts @@ -24,7 +24,7 @@ export const pageDescriptions: Record = { projects: "Manage projects within teams", "access-groups": "Manage access groups for role-based permissions", budgets: "Set and monitor spending budgets", - api_ref: "Browse API documentation and endpoints", + "api-reference": "Browse API documentation and endpoints", "model-hub-table": "Explore available AI models and providers", "learning-resources": "Access tutorials and documentation", caching: "Configure response caching settings", From 895951744ddbf62ef31a78b86d953d7f6eb6cefb Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 19 Mar 2026 13:36:21 -0700 Subject: [PATCH 2/4] Fix leftnav navigation regression and useProxySettings initial state The leftnav was updated to emit page="api-reference" but only "api_ref" was in LEGACY_REDIRECTS, causing clicks to fall through to the default Usage page. Add "api-reference" entry to the redirect map. Also include LITELLM_UI_API_DOC_BASE_URL in the hook's initial state to avoid a brief flash of incorrect base URL. Co-Authored-By: Claude Opus 4.6 --- .../src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts | 1 + ui/litellm-dashboard/src/app/page.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts index 62de696b7a..d4fb307385 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/proxySettings/useProxySettings.ts @@ -7,6 +7,7 @@ export default function useProxySettings() { const [proxySettings, setProxySettings] = useState({ PROXY_BASE_URL: "", PROXY_LOGOUT_URL: "", + LITELLM_UI_API_DOC_BASE_URL: null as string | null, }); useEffect(() => { diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 7ee48317e9..5f545b635e 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -81,6 +81,7 @@ interface ProxySettings { */ const LEGACY_REDIRECTS: Record = { api_ref: "api-reference", + "api-reference": "api-reference", }; function CreateKeyPageContent() { From e9e5ed989c29103ab2af839add0a1e822553820b Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 19 Mar 2026 13:59:45 -0700 Subject: [PATCH 3/4] Move legacy redirect from render phase to useEffect router.replace was called directly during render, which is unsafe in React 18 concurrent mode. Move it into a useEffect and use a computed flag (isLegacyRedirect) to show LoadingScreen while redirecting. Co-Authored-By: Claude Opus 4.6 --- ui/litellm-dashboard/src/app/page.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 5f545b635e..3aac962135 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -253,6 +253,15 @@ function CreateKeyPageContent() { } }, [redirectToLogin]); + // Redirect legacy query-param pages to their new path-based routes + const isLegacyRedirect = page in LEGACY_REDIRECTS; + useEffect(() => { + if (isLegacyRedirect) { + const base = (proxyBaseUrl || "") + "/ui"; + router.replace(`${base}/${LEGACY_REDIRECTS[page]}`); + } + }, [isLegacyRedirect, page, router]); + // Check for a stored return URL after successful authentication // This handles the case where user comes back from SSO and we need to redirect to the original URL useEffect(() => { @@ -437,14 +446,7 @@ function CreateKeyPageContent() { setShowClaudeCodePrompt(true); }; - if (authLoading || redirectToLogin) { - return ; - } - - // Redirect legacy query-param pages to their new path-based routes - if (page in LEGACY_REDIRECTS) { - const base = (proxyBaseUrl || "") + "/ui"; - router.replace(`${base}/${LEGACY_REDIRECTS[page]}`); + if (authLoading || redirectToLogin || isLegacyRedirect) { return ; } From 2e70c23307d85c7430bdf0523204863865797d79 Mon Sep 17 00:00:00 2001 From: yuneng-jiang Date: Thu, 19 Mar 2026 14:05:56 -0700 Subject: [PATCH 4/4] Gate legacy redirect on authLoading to ensure proxyBaseUrl is resolved The redirect useEffect fires before getUiConfig() completes, so proxyBaseUrl is always "" on first render. Gate on !authLoading so the redirect only fires after config is fetched, matching the pattern used by the login redirect. Co-Authored-By: Claude Opus 4.6 --- ui/litellm-dashboard/src/app/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 3aac962135..03431428ad 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -256,11 +256,11 @@ function CreateKeyPageContent() { // Redirect legacy query-param pages to their new path-based routes const isLegacyRedirect = page in LEGACY_REDIRECTS; useEffect(() => { - if (isLegacyRedirect) { + if (!authLoading && isLegacyRedirect) { const base = (proxyBaseUrl || "") + "/ui"; router.replace(`${base}/${LEGACY_REDIRECTS[page]}`); } - }, [isLegacyRedirect, page, router]); + }, [authLoading, isLegacyRedirect, page, router]); // Check for a stored return URL after successful authentication // This handles the case where user comes back from SSO and we need to redirect to the original URL