Skip to content

migrate shadcn components from Radix UI to Base UI#272

Open
jrhizor wants to merge 6 commits into
mainfrom
jrhizor/shadcn-base-ui-migration
Open

migrate shadcn components from Radix UI to Base UI#272
jrhizor wants to merge 6 commits into
mainfrom
jrhizor/shadcn-base-ui-migration

Conversation

@jrhizor
Copy link
Copy Markdown
Contributor

@jrhizor jrhizor commented May 12, 2026

Closes #104.

Summary

  • Regenerated all @workspace/ui components from shadcn's base-vega registry (pnpm dlx shadcn@latest add --overwrite …) so they're now built on @base-ui/react instead of @radix-ui/*.
  • Removed every @radix-ui/* package (12) plus the radix-ui meta-package; added @base-ui/react@^1.4.1. Net -31 / +3 packages in the lockfile.
  • Updated 31 consumer files (apps/web, apps/www, packages/whitelabel, packages/docs, plus the custom tags-input in packages/ui) for the API differences below.

Consumer API changes

Before (Radix) After (Base UI)
<X asChild><Y/></X> <X render={<Y/>}/> (53 call sites)
data-[state=open]:… on a trigger data-popup-open:…
w-(--radix-dropdown-menu-trigger-width) w-(--anchor-width) (or just drop — Base UI's popup already sizes to anchor)
<PopoverContent onOpenAutoFocus={(e) => e.preventDefault()}/> <PopoverContent initialFocus={false}/>

Other changes

  • form.tsx deleted — no consumers, superseded by base-vega's field.tsx (added but not yet adopted).
  • input-group.tsx added (peer of input in the new registry).
  • calendar.tsx: tablemonth_grid for react-day-picker v10.

Test plan

  • pnpm check-types passes in apps/web and apps/www
  • pnpm exec tsc --noEmit passes in packages/ui, packages/whitelabel, packages/docs
  • pnpm exec biome lint passes for all migration-touched files (the only remaining lint warnings — useImportType in app-sidebar.tsx / full-page-card.tsx, unused promptName in history-button.tsx, and CSS parser warnings in styles.css — are pre-existing on main)
  • Manual smoke test of the major shadcn-driven flows: open/close a dropdown menu, popover, dialog, sheet; toggle a checkbox + switch; click a tooltip trigger; navigate via sidebar links; load a page with the filter bar.

Regenerated all shadcn components in @workspace/ui from the new base-vega
registry (`pnpm dlx shadcn@latest add --overwrite ...`). All @radix-ui/*
packages and the radix-ui meta-package removed; @base-ui/react added.

Consumer updates:
- asChild → render={...} prop across 53 call sites
- data-[state=open] → data-popup-open (Base UI's open-state attribute)
- --radix-* CSS vars → Base UI equivalents (--anchor-width)
- PopoverContent onOpenAutoFocus → initialFocus={false}

Other changes:
- form.tsx removed (no consumers; superseded by base-vega's field.tsx,
  which is added but not yet adopted)
- input-group.tsx added as a peer of input
- calendar.tsx fixed for react-day-picker v10 (`table` → `month_grid`)
@vercel
Copy link
Copy Markdown

vercel Bot commented May 12, 2026

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

Project Deployment Actions Updated (UTC)
elmo Ready Ready Preview, Comment May 12, 2026 8:44pm

Request Review

prompt-wizard.tsx calls useQueryClient() (added in #262) but the
stories never set up a QueryClient. Add a global decorator in
.storybook/preview that provides a fresh client per story, with
retries disabled so error-state stories surface immediately.
The mock was rendering as <button>, which has intrinsic content sizing —
even with display:flex it doesn't stretch to fill its flex parent the
way an anchor does. Visible on Switch Brand in the nav-user dropdown
(other items in the same menu render as <a> or <div> and stretch fine).

The real Link in production renders as <a>, so this just brings the
mock in line with production layout.
Resolve conflicts:
- apps/web/.storybook/preview.tsx — take main's simpler QueryClient
  decorator (shared client with staleTime: Infinity); same intent as the
  one this branch added.
- apps/web/src/stories/_mocks/tanstack-router.tsx — auto-merged keeps
  main's new ScriptOnce export and this branch's <a>-based Link mock.
- pnpm-lock.yaml — take main's; this branch's deps reinstall cleanly.

Also fix two warnings that surface under main's new Storybook vitest
addon:
- packages/ui/src/components/tags-input.tsx — set nativeButton={false}
  on the combobox PopoverTrigger (renders a <div>, not a button).
- packages/ui/src/components/button.tsx — infer nativeButton from the
  render prop's element type so the common <Button render={<Link/>}>
  and <Button render={<a/>}> patterns don't all need to spell out
  nativeButton={false}.
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.

move from shadcn radix -> base

1 participant