Skip to content

refactor: migrate Ant Design notifications to use App.useApp() cont…#20549

Merged
yuneng-jiang merged 3 commits intoBerriAI:mainfrom
swayambhu94:fix/ui/antd-notification
Feb 6, 2026
Merged

refactor: migrate Ant Design notifications to use App.useApp() cont…#20549
yuneng-jiang merged 3 commits intoBerriAI:mainfrom
swayambhu94:fix/ui/antd-notification

Conversation

@swayambhu94
Copy link
Contributor

@swayambhu94 swayambhu94 commented Feb 6, 2026

…ext via a new global provider.

Relevant issues

Pre-Submission checklist

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

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🐛 Bug Fix
🧹 Refactoring

Changes

1. [notifications_manager.tsx] - Hybrid notification approach:

  • Added notificationInstance variable to store the context-based instance
  • Added [setNotificationInstance()] function to inject the instance from context
  • Created [getNotification()]helper that prefers context instance, falls back to static
  • Added exported COMMON_NOTIFICATION_PROPS with showProgress: true and pauseOnHover: true
  • All notification methods now spread COMMON_NOTIFICATION_PROPS

2. [AntdGlobalProvider.tsx] - New context provider:

  • Wraps app with Antd's <App> component
  • Uses App.useApp() hook to get the context-based notification instance
  • Injects it into NotificationManager via [setNotificationInstance()]
Screenshot 2026-02-06 at 9 45 44 AM Screenshot 2026-02-06 at 9 46 36 AM

…ext via a new global provider.

1.
notifications_manager.tsx
 - Hybrid notification approach:

Added notificationInstance variable to store the context-based instance
Added
setNotificationInstance()
 function to inject the instance from context
Created
getNotification()
 helper that prefers context instance, falls back to static
Added COMMON_NOTIFICATION_PROPS (exported) with showProgress: true and pauseOnHover: true
All notification methods (
error
,
warning
,
info
,
success
,
fromBackend
) now spread COMMON_NOTIFICATION_PROPS
2.
AntdGlobalProvider.tsx
 - New context provider:

Wraps app with Antd's <App> component
Uses App.useApp() hook to get the context-based notification instance
Injects it into NotificationManager via
setNotificationInstance()
@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 6, 2026 10:06am

Request Review

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Greptile Overview

Greptile Summary

This PR migrates the dashboard’s Ant Design notification usage toward the App.useApp() context API by introducing a new AntdGlobalProvider (wrapping the app in <App>) and updating NotificationManager to prefer an injected, context-derived notification instance with a fallback to the static notification export.

The main behavioral change is that notifications can now be driven by the Antd context, but the implementation uses a module-level mutable singleton to store the instance, which makes notification routing depend on render/mount order and can be problematic in SSR/multi-user contexts.

Confidence Score: 3/5

  • This PR is moderately safe to merge but has architectural risks around global mutable state and root-level client boundaries.
  • Changes are relatively small and isolated to notification plumbing, but the new module-level notification singleton plus root layout wrapping can introduce hard-to-debug SSR/client boundary and ordering issues.
  • ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx; ui/litellm-dashboard/src/app/layout.tsx

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/app/layout.tsx Wraps app children in new AntdGlobalProvider; introduces a root client boundary which can affect SSR/perf across the whole dashboard.
ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx Refactors notifications to prefer a context-provided instance, but stores it in a module-level mutable singleton which can cause SSR/user bleed and order-dependent behavior.
ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx Adds a new client-side Antd provider and injects App.useApp().notification into NotificationManager; effect may repeatedly set a global instance depending on hook stability.

Sequence Diagram

sequenceDiagram
  participant RootLayout as RootLayout (layout.tsx)
  participant Provider as AntdGlobalProvider
  participant App as Antd <App>
  participant Init as AntdAppInit
  participant NM as NotificationManager
  participant Cmp as Any UI component

  RootLayout->>Provider: render(children)
  Provider->>App: wrap subtree
  App-->>Init: App.useApp() provides notification API
  Init->>NM: setNotificationInstance(notification)
  Cmp->>NM: NotificationManager.error/warning/info/success/fromBackend()
  NM->>NM: getNotification()
  alt instance injected
    NM-->>App: use context notification instance
  else not injected yet
    NM-->>NM: fallback to staticNotification
  end
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +7 to +14
let notificationInstance: NotificationInstance | null = null;

export const setNotificationInstance = (instance: NotificationInstance) => {
notificationInstance = instance;
};

// Helper to get the best available notification instance
const getNotification = () => notificationInstance || staticNotification;
Copy link
Contributor

Choose a reason for hiding this comment

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

Global mutable instance

notificationInstance is a module-level mutable singleton that gets set by setNotificationInstance() and then used by getNotification(). In Next.js/React this can lead to cross-request/user bleed in SSR/hydration scenarios (the module is shared), and also makes notification behavior depend on mount order (calls before AntdAppInit runs will silently use the static API). A safer pattern is to keep notifications entirely context-driven (or pass the instance explicitly) rather than storing it in a global variable.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx
Line: 7:14

Comment:
**Global mutable instance**

`notificationInstance` is a module-level mutable singleton that gets set by `setNotificationInstance()` and then used by `getNotification()`. In Next.js/React this can lead to cross-request/user bleed in SSR/hydration scenarios (the module is shared), and also makes notification behavior depend on mount order (calls before `AntdAppInit` runs will silently use the static API). A safer pattern is to keep notifications entirely context-driven (or pass the instance explicitly) rather than storing it in a global variable.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +8 to +13
const AntdAppInit = () => {
const { notification } = App.useApp();

useEffect(() => {
setNotificationInstance(notification);
}, [notification]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Effect runs every render

useEffect(() => setNotificationInstance(notification), [notification]) will re-run whenever App.useApp() returns a new notification object reference. If the hook returns a new object each render (implementation-dependent), this can cause repeated global writes and potential notification instance churn. Consider ensuring the instance is stable (e.g., only set once on mount) or guard against redundant sets.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx
Line: 8:13

Comment:
**Effect runs every render**

`useEffect(() => setNotificationInstance(notification), [notification])` will re-run whenever `App.useApp()` returns a new `notification` object reference. If the hook returns a new object each render (implementation-dependent), this can cause repeated global writes and potential notification instance churn. Consider ensuring the instance is stable (e.g., only set once on mount) or guard against redundant sets.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Additional Comments (1)

ui/litellm-dashboard/src/app/layout.tsx
Client provider in layout

RootLayout is a Server Component by default, but it now renders AntdGlobalProvider (a Client Component). This forces a client boundary at the root and may impact SSR behavior/perf for the entire app. If the intent is only to provide Antd notifications, consider scoping the client provider to the smallest subtree that needs it, rather than wrapping all children at the root layout.

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/litellm-dashboard/src/app/layout.tsx
Line: 15:25

Comment:
**Client provider in layout**

`RootLayout` is a Server Component by default, but it now renders `AntdGlobalProvider` (a Client Component). This forces a client boundary at the root and may impact SSR behavior/perf for the entire app. If the intent is only to provide Antd notifications, consider scoping the client provider to the smallest subtree that needs it, rather than wrapping all `children` at the root layout.

How can I resolve this? If you propose a fix, please make it concise.

@yuneng-jiang yuneng-jiang merged commit a4689c9 into BerriAI:main Feb 6, 2026
4 of 8 checks passed
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.

3 participants