refactor(*): migrate 'radix-ui' primitives to 'base-ui' - #1123
Conversation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
tanstack-com | b4849ed | Commit Preview URL Branch Preview URL |
Aug 06 2026, 04:42 AM |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Replaces all
@radix-uiprimitives with Base UI and removes the three@radix-ui/react-{dialog,dropdown-menu,tooltip}dependencies.Component mapping
Dialog.OverlayDialog.BackdropDialog.ContentDialog.PopupDropdownMenu.ContentMenu.Positioner+Menu.PopupTooltip.ContentTooltip.Positioner+Tooltip.PopupTooltip.ProviderdelayDuration/skipDelayDurationdelay/timeoutDropdownMenu.ItemonSelectMenu.ItemonClickonSelect+e.preventDefault()closeOnClick={false}onInteractOutside+preventDefault()onOpenChange+eventDetails.cancel()data-[state=closed]data-ending-style/data-starting-style--radix-*-transform-origin--transform-origin--radix-dropdown-menu-content-available-height--available-heightfocus:styling on itemsdata-highlighted:asChild→renderThe dropdown adapters drop Radix's
asChildboolean in favour of Base UI'srenderelement prop.On triggers
renderis required andchildrenis not in the type. Thatmatters: 22 of 24
<DropdownTrigger>call sites passed noasChildprop atall and silently relied on its
= truedefault, so a boolean rename wouldhave flipped them to wrapper mode with no compile error. Making
renderrequired turns every one of those sites into a type error instead.
On items
renderstays optional andchildrenis kept — their previousdefault was already falsy, so wrapper-mode call sites are unchanged.
DropdownTriggeralso gains an optionalnativeButtonpassthrough.BrandContextMenuneedsnativeButton={false}because its trigger renders aposition: fixedvirtual anchor<span>rather than a native<button>.Radix-only workarounds removed
SearchModalno longer needs the machinery that worked around Radixunmounting content during its exit animation — Base UI keeps popups mounted
through it natively. Removed:
forceMount, theshouldRenderSearchmountgate,
searchModalTransitionMs, and thedocument.body.style.pointerEventssave/restore block.
That last one was not merely redundant: Radix portaled content outside the
body's pointer-events scope, Base UI does not. Left in place it rendered the
search modal visible but unclickable.
Radix DOM contracts consumed outside the migrated files
Radix's
data-stateand--radix-*are a DOM contract, so anything readingthem breaks when the primitive is swapped — with no compile error. Three
places consumed them:
src/styles/app.cssanimation selectors for.cart-panel,.cart-overlay,.search-modal-overlay,.search-modal-contentand.dropdown-contentmove from
[data-state='open'|'closed']to[data-open]/[data-ending-style].PackagePillssized its menu with--radix-dropdown-menu-content-available-height, which no longer resolves;now
--available-height. Confirmed in the browser that Base UI sets it onthe positioner and it inherits to the popup.
LibraryLayoutkeeps the docs sidebar open while a dropdown launched frominside it is active, via
expandedMenuRef.current?.querySelector('[data-state="open"]'). Base UImarks the open trigger with
data-popup-open; the popup itself isportaled to
bodyand so is out of that subtree, which rules out[data-open]here. Verified against the live sidebar: the new selectormatches and the old one does not.
The last two were found while merging
main, not bytsc— which is theconcrete form of the silent-failure risk this section describes.
Other
npm-stats/dropdown-menu.tsx→DropdownMenu.tsxto match the PascalCaseconvention used by every other component file in the repo.
/ds/dropdown,/ds/cardsand/ds/buttonscopy and code samples updatedto reference Base UI.
Verification
tsc, lint and the test suite pass.Exercised in the browser: blog author filter (open → select → filter
applied),
/ds/dropdown,/ds/buttonssplit button, navbar social menu,CopyPageDropdown, breadcrumb TOC,BrandContextMenu(anchors at cursor),and
SearchModal— including that its framework filter opens inside themodal without dismissing it. Console clean.
The docs sidebar guard was checked by opening the sidebar's own
FrameworkSelectand evaluating both selectors against that subtree:[data-popup-open]matches,[data-state="open"]does not.Not exercised locally: the npm-stats and charts dropdowns (those pages call
the external npm API and error out locally),
AuthenticatedUserMenu(needsauth), and the
LoginModal/AvatarCropModal/CartDrawer/LibrariesOverlaydialogs.