Skip to content

refactor(*): migrate 'radix-ui' primitives to 'base-ui' - #1123

Draft
sukvvon wants to merge 4 commits into
mainfrom
refactor/radix-to-base-ui
Draft

refactor(*): migrate 'radix-ui' primitives to 'base-ui'#1123
sukvvon wants to merge 4 commits into
mainfrom
refactor/radix-to-base-ui

Conversation

@sukvvon

@sukvvon sukvvon commented Aug 6, 2026

Copy link
Copy Markdown
Member

Replaces all @radix-ui primitives with Base UI and removes the three
@radix-ui/react-{dialog,dropdown-menu,tooltip} dependencies.

Component mapping

Radix Base UI
Dialog.Overlay Dialog.Backdrop
Dialog.Content Dialog.Popup
DropdownMenu.Content Menu.Positioner + Menu.Popup
Tooltip.Content Tooltip.Positioner + Tooltip.Popup
Tooltip.Provider delayDuration/skipDelayDuration delay/timeout
DropdownMenu.Item onSelect Menu.Item onClick
onSelect + 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-height
focus: styling on items data-highlighted:

asChildrender

The dropdown adapters drop Radix's asChild boolean in favour of Base UI's
render element prop.

On triggers render is required and children is not in the type. That
matters: 22 of 24 <DropdownTrigger> call sites passed no asChild prop at
all and silently relied on its = true default, so a boolean rename would
have flipped them to wrapper mode with no compile error. Making render
required turns every one of those sites into a type error instead.

On items render stays optional and children is kept — their previous
default was already falsy, so wrapper-mode call sites are unchanged.

DropdownTrigger also gains an optional nativeButton passthrough.
BrandContextMenu needs nativeButton={false} because its trigger renders a
position: fixed virtual anchor <span> rather than a native <button>.

Radix-only workarounds removed

SearchModal no longer needs the machinery that worked around Radix
unmounting content during its exit animation — Base UI keeps popups mounted
through it natively. Removed: forceMount, the shouldRenderSearch mount
gate, searchModalTransitionMs, and the document.body.style.pointerEvents
save/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-state and --radix-* are a DOM contract, so anything reading
them breaks when the primitive is swapped — with no compile error. Three
places consumed them:

  • src/styles/app.css animation selectors for .cart-panel, .cart-overlay,
    .search-modal-overlay, .search-modal-content and .dropdown-content
    move from [data-state='open'|'closed'] to [data-open] /
    [data-ending-style].
  • PackagePills sized 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 on
    the positioner and it inherits to the popup.
  • LibraryLayout keeps the docs sidebar open while a dropdown launched from
    inside it is active, via
    expandedMenuRef.current?.querySelector('[data-state="open"]'). Base UI
    marks the open trigger with data-popup-open; the popup itself is
    portaled to body and so is out of that subtree, which rules out
    [data-open] here. Verified against the live sidebar: the new selector
    matches and the old one does not.

The last two were found while merging main, not by tsc — which is the
concrete form of the silent-failure risk this section describes.

Other

  • npm-stats/dropdown-menu.tsxDropdownMenu.tsx to match the PascalCase
    convention used by every other component file in the repo.
  • /ds/dropdown, /ds/cards and /ds/buttons copy and code samples updated
    to 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/buttons split button, navbar social menu,
CopyPageDropdown, breadcrumb TOC, BrandContextMenu (anchors at cursor),
and SearchModal — including that its framework filter opens inside the
modal without dismissing it. Console clean.

The docs sidebar guard was checked by opening the sidebar's own
FrameworkSelect and 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 (needs
auth), and the LoginModal / AvatarCropModal / CartDrawer /
LibrariesOverlay dialogs.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2cb86ebd-ad95-4f82-8d02-95cead2edd87

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 6, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

@socket-security

socket-security Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​base-ui/​react@​1.7.0891008995100

View full report

@sukvvon sukvvon self-assigned this Aug 6, 2026
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.

1 participant