refactor(ui): switch shadcn primitives from Radix to Base UI - #32124
Conversation
shadcn made Base UI the default primitive library in July 2026 and our only shadcn component so far is the Button canary, so this is the last cheap moment to switch before the primitives phase adds the full set. components.json style moves from new-york (a legacy alias that resolves to the Radix variant) to base-vega. Button is regenerated from the base-vega registry with the same local adaptations as before: cva beta object form via lib/cva.config and a React 18 forwardRef wrapper. The polymorphic asChild prop becomes Base UI's render prop. radix-ui is replaced by @base-ui/react 1.6.0. Base UI optionally peers on date-fns 4 while tremor pins 3, so date-fns is bumped to 4.4.0 with an npm override; our only usage (add) is API-identical and the override can go away when tremor does.
📝 WalkthroughWalkthroughThis PR migrates the dashboard's Button component from Radix Slot-based composition to base-ui/react's ButtonPrimitive. It updates package.json dependencies (adds ChangesButton migration to base-ui
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Consumer
participant Button
participant ButtonPrimitive
Consumer->>Button: render with variant/size/render props
Button->>Button: compute className via buttonVariants
Button->>ButtonPrimitive: pass data-slot, className, ref, props
ButtonPrimitive-->>Consumer: rendered element (native button or custom via render prop)
Related Issues: None specified. Related PRs: None specified. Suggested labels: ui, dependencies, frontend Suggested reviewers: None specified. 🐰 A hop, a skip, from Radix to base, 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Greptile SummaryThis PR completes the migration of the shadcn UI foundation from Radix UI to Base UI 1.x, updating
Confidence Score: 5/5The change is a contained UI primitive swap with no backend or auth surface modifications; the only production risk is visual regressions in components that are already tested. All 12 UI components are migrated consistently, Radix references are fully removed, call sites are updated throughout the chat feature, and new/updated tests cover the converted surfaces. The two findings are cosmetic: dead Radix selectors in the tooltip class string, and the Tabs orientation prop not forwarded to TabsPrimitive.Root (only affects vertical tab layouts, none of which are used in the current codebase). ui/litellm-dashboard/src/components/ui/tabs.tsx — the orientation prop is extracted but not passed to TabsPrimitive.Root, so data-horizontal/data-vertical CSS rules will not apply if vertical tabs are ever added.
|
| Filename | Overview |
|---|---|
| ui/litellm-dashboard/src/components/ui/tabs.tsx | Migrated from Radix to Base UI Tabs; orientation prop is destructured but not forwarded to TabsPrimitive.Root, leaving data-horizontal/data-vertical CSS selectors inert for non-default orientations. |
| ui/litellm-dashboard/src/components/ui/tooltip.tsx | Migrated from Radix to Base UI Tooltip; correctly adopts Positioner/Popup split and delay prop, but retains dead data-[state=delayed-open] selectors from the old Radix template. |
| ui/litellm-dashboard/src/components/ui/button.tsx | Swapped Radix Slot for Base UI ButtonPrimitive; removed asChild in favour of the render prop; cva variants and React 18 forwardRef wrapper preserved correctly. |
| ui/litellm-dashboard/src/components/ui/alert-dialog.tsx | Clean migration to Base UI AlertDialog; Overlay→Backdrop, Content→Popup, Action/Cancel both map to Close (correct for Base UI API). |
| ui/litellm-dashboard/src/components/ui/dialog.tsx | Clean migration to Base UI Dialog; Overlay→Backdrop, Content→Popup, close button now uses render prop composition. |
| ui/litellm-dashboard/src/components/ui/select.tsx | Migrated to Base UI Select; ScrollUpButton/ScrollDownButton replaced with ScrollUpArrow/ScrollDownArrow, SelectLabel updated to GroupLabel. |
| ui/litellm-dashboard/package.json | radix-ui removed, @base-ui/react@^1.6.0 added, date-fns bumped to ^4.4.0 with override; security pins preserved in the overrides block. |
Reviews (3): Last reviewed commit: "fix(ui): close the alert dialog when Ale..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
ui/litellm-dashboard/src/components/ui/button.test.tsx (1)
32-38: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueUse a neutral element here instead of
<a>
This test can demonstratenativeButton={false}without modeling the discouraged link-as-button pattern; a<span>or<div>keeps the example aligned with Base UI guidance.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@ui/litellm-dashboard/src/components/ui/button.test.tsx` around lines 32 - 38, The Button render-prop test currently uses an anchor element, which models a discouraged link-as-button pattern. Update the test in button.test.tsx to use a neutral element such as a span or div with Button nativeButton={false} and render, while keeping the same button-role assertions and class/href expectations that verify the Button component behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@ui/litellm-dashboard/package.json`:
- Around line 39-46: The package override is forcing date-fns v4 into Tremor’s
dependency tree even though `@tremor/react`@3.18.7 expects date-fns v3, which can
break date components at runtime. Update the package.json dependency/override
section to either remove the date-fns ^4.4.0 override or change Tremor to a
version that is compatible with date-fns v4, and verify the dependency
resolution around `@tremor/react` stays on a supported date-fns major version.
---
Nitpick comments:
In `@ui/litellm-dashboard/src/components/ui/button.test.tsx`:
- Around line 32-38: The Button render-prop test currently uses an anchor
element, which models a discouraged link-as-button pattern. Update the test in
button.test.tsx to use a neutral element such as a span or div with Button
nativeButton={false} and render, while keeping the same button-role assertions
and class/href expectations that verify the Button component behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d3a14083-ff02-4826-80f0-9bb1d8db30d2
⛔ Files ignored due to path filters (1)
ui/litellm-dashboard/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (4)
ui/litellm-dashboard/components.jsonui/litellm-dashboard/package.jsonui/litellm-dashboard/src/components/ui/button.test.tsxui/litellm-dashboard/src/components/ui/button.tsx
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
…itellm_shadcn_base_ui
The chat UI migration landed 11 components/ui files generated against the old Radix registry config after this branch cut over to Base UI, which would have left them importing a deleted package. All 11 (dialog, alert-dialog, select, popover, tooltip, tabs, switch, scroll-area, collapsible, separator, label) are regenerated from the base-vega registry, with the repo conventions re-applied where relevant (cva beta object form from lib/cva.config in tabs; the Button canary keeps its React 18 forwardRef adaptation). Chat feature call sites move from the Radix asChild pattern to Base UI's render prop, and TooltipProvider delayDuration becomes delay.
…rride The date-fns 4 override was written by replacing the whole overrides object, dropping the ten security pins (prismjs, js-yaml, glob, minimatch, lodash, ws, braces, axios, postcss, esbuild) that keep patched versions in the lockfile; osv-scan caught the vulnerable versions resurfacing. Restores the pins alongside date-fns and regenerates the lockfile.
|
@greptileai re review |
The date-fns 4 override forces react-day-picker 8 (authored against v3) onto v4 at runtime, which a build or lint pass cannot validate. This renders the shared UsageDatePicker wrapper, opens the calendar, checks the month grid, and selects a day, so a date-fns API break in the tremor date path fails tests instead of throwing in production. Delete alongside the override when tremor is removed.
The base-vega registry template renders AlertDialogAction as a plain Button with no Close binding, so confirm buttons fired their onClick but left the dialog open; both consumers (conversation delete, MCP credential revoke) were written against the Radix semantics where Action dismisses on click. Binds Action to AlertDialogPrimitive.Close via the render prop, mirroring AlertDialogCancel, and pins the behavior with a test so a future shadcn add --overwrite cannot silently reintroduce the template's non-closing Action.
|
@greptileai re review |
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays 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
The chat page is the main observable surface since its components were converted:
cd ui/litellm-dashboard && npm install(resolves cleanly, no peer flags needed)npx vitest run src/components/ui/button.test.tsx(5 tests, including the Base UI render-prop polymorphism and React 18 ref forwarding)npm run devand load the chat page; exercise the converted surfaces: model selector popover, MCP servers popover, tooltip hover on message copy/edit buttons, conversation rename/delete (alert dialog), and the MCP credentials revoke dialogType
🧹 Refactoring
Changes
shadcn made Base UI the default primitive library in July 2026 (https://ui.shadcn.com/docs/changelog/2026-07-base-ui-default). Radix, the previous default, has been in maintenance slowdown since its maintainers moved on, and Base UI 1.x is built by the same lineage of authors (ex-Radix and Floating UI, at MUI). Our shadcn foundation (#31995) landed with the Radix variant because that was the default at the time; with exactly one shadcn component in the tree (the Button canary), this is the last cheap moment to switch before the primitives phase adds the full component set
components.jsonstyle moves fromnew-york(a legacy alias that now resolves to the Radix variantradix-vega) tobase-vega, so every futureshadcn addpulls Base UI components. Button is regenerated from the base-vega registry viashadcn add button --overwriteand re-adapted the same way the original was: cva beta object form imported fromlib/cva.configinstead ofclass-variance-authority, and a React 18forwardRefwrapper since registry templates assume React 19. The polymorphicasChildprop is replaced by Base UI'srenderprop; per Base UI semantics, rendering a non-button element requiresnativeButton={false}and keepsrole=buttonon the rendered element, which the updated test pinsradix-uiis replaced by@base-ui/react1.6.0 (supports React 17/18/19). Base UI optionally peers on date-fns 4 while tremor hard-pins 3 through react-day-picker 8, which is unsolvable against a single hoisted copy, so date-fns is bumped to 4.4.0 with an npm override. Our only direct usage isadd()inkeyExpiryUtils.ts, which is API-identical across 3 and 4 (v4 only adds timezone support), and the override can be deleted when tremor is removedScope update: chat UI components converted too
After this branch was cut, the chat UI migration (11314f4) landed on the base branch with 11 components/ui files generated against the old Radix registry config (dialog, alert-dialog, select, popover, tooltip, tabs, switch, scroll-area, collapsible, separator, label). Merged as-is, this PR would have deleted the radix-ui package out from under them, so the conversion is now part of the switch: all 11 are regenerated from the base-vega registry via
shadcn add --overwrite, tabs gets the same cva-beta adaptation as Button, and the chat feature's call sites move from Radix'sasChildcomposition to Base UI'srenderprop (TooltipProvider delayDurationbecomesdelay). Component tests for the chat surfaces pass and the tree compiles with zero radix references leftSummary by CodeRabbit