Conversation
# Conflicts: # package.json # pnpm-lock.yaml
Update the test app template to shadcn v4, expand registry items so admin and rich-text-input embed the Base UI wrappers they rely on, and adjust breadcrumb drawer close usage so generated registry apps build correctly.
There was a problem hiding this comment.
Pull request overview
This PR migrates shadcn-admin-kit from Radix UI to Base UI (aligned with shadcn v4), regenerating the UI wrappers and updating admin/website components to the new headless patterns while removing Radix dependencies.
Changes:
- Replaced Radix-based UI primitives (tooltip, dropdown/menu, dialogs, navigation menu, etc.) with Base UI equivalents across
src/andwebsite/. - Updated styling/theme inputs (Tailwind imports, Inter variable font, radius tokens) and regenerated shadcn wrappers.
- Updated registry/build tooling and metadata for shadcn v4.
Reviewed changes
Copilot reviewed 84 out of 85 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/components/ui/tooltip.tsx | Tooltip wrapper migrated to Base UI. |
| website/src/components/ui/navigation-menu.tsx | Navigation menu wrapper migrated to Base UI patterns. |
| website/src/components/ui/dropdown-menu.tsx | Dropdown menu wrapper migrated to Base UI Menu. |
| website/src/components/ui/button.tsx | Button wrapper migrated to Base UI Button. |
| website/src/components/Users.tsx | Updated link buttons to Base UI render pattern. |
| website/src/components/Pricing.tsx | Updated CTA button to Base UI render pattern. |
| website/src/components/HotspotSvg.tsx | Updated tooltip trigger usage to Base UI render pattern. |
| website/src/components/Hero.tsx | Updated hero CTA buttons to Base UI render pattern. |
| website/src/components/Header.tsx | Updated header menus/buttons to Base UI render pattern. |
| website/src/components/CallToAction.tsx | Updated CTA button to Base UI render pattern. |
| website/src/components/Backends.tsx | Updated CTA button to Base UI render pattern. |
| website/src/components/AdvancedCapabilities.tsx | Updated CTA button to Base UI render pattern. |
| src/lib/utils.ts | Formatting update (semicolons removed). |
| src/index.css | Added shadcn v4 Tailwind import + Inter font + radius tokens. |
| src/hooks/use-mobile.ts | Formatting update (semicolons removed). |
| src/components/ui/tooltip.tsx | Tooltip wrapper migrated to Base UI Tooltip. |
| src/components/ui/toggle.tsx | Toggle wrapper migrated to Base UI Toggle. |
| src/components/ui/toggle-group.tsx | ToggleGroup wrapper migrated to Base UI ToggleGroup. |
| src/components/ui/textarea.tsx | Textarea styles adjusted. |
| src/components/ui/tabs.tsx | Tabs wrapper migrated to Base UI Tabs + added variants. |
| src/components/ui/table.tsx | Table marked client + minor class adjustments. |
| src/components/ui/switch.tsx | Switch wrapper migrated to Base UI Switch + size support. |
| src/components/ui/sonner.tsx | Toaster marked client + icons + styling options. |
| src/components/ui/skeleton.tsx | Skeleton styling adjusted. |
| src/components/ui/sidebar.tsx | Sidebar reworked to Base UI render/mergeProps patterns. |
| src/components/ui/sheet.tsx | Sheet migrated from Radix Dialog to Base UI Dialog. |
| src/components/ui/separator.tsx | Separator migrated to Base UI Separator with decorative handling. |
| src/components/ui/select.tsx | Select migrated to Base UI Select with positioner/popup structure. |
| src/components/ui/radio-group.tsx | RadioGroup migrated to Base UI Radio/RadioGroup. |
| src/components/ui/popover.tsx | Popover migrated to Base UI Popover + new header/title/description helpers. |
| src/components/ui/pagination.tsx | Pagination links switched to Button + render anchor pattern. |
| src/components/ui/navigation-menu.tsx | Navigation menu wrapper migrated to Base UI patterns. |
| src/components/ui/label.tsx | Label simplified to native <label>. |
| src/components/ui/input.tsx | Input migrated to Base UI Input. |
| src/components/ui/input-group.tsx | New InputGroup component added. |
| src/components/ui/dropdown-menu.tsx | Dropdown menu migrated to Base UI Menu with submenu/indicators. |
| src/components/ui/drawer.tsx | Drawer styles adjusted (vaul-based). |
| src/components/ui/dialog.tsx | Dialog migrated to Base UI Dialog + close-button rendering. |
| src/components/ui/command.tsx | Command palette styling and input rebuilt using InputGroup. |
| src/components/ui/checkbox.tsx | Checkbox migrated to Base UI Checkbox. |
| src/components/ui/card.tsx | Card styles/structure updated + size support. |
| src/components/ui/button.tsx | Button migrated to Base UI Button + variant/size updates. |
| src/components/ui/breadcrumb.tsx | Breadcrumb migrated to Base UI render utilities (useRender/mergeProps). |
| src/components/ui/badge.tsx | Badge migrated to Base UI render utilities (useRender/mergeProps). |
| src/components/ui/avatar.tsx | Avatar migrated to Base UI Avatar + group/badge additions. |
| src/components/ui/alert.tsx | Alert styles updated + added AlertAction slot. |
| src/components/ui/accordion.tsx | Accordion migrated to Base UI Accordion. |
| src/components/rich-text-input/minimal-tiptap/extensions/image/components/image-view-block.tsx | Replaced Radix icons with lucide icons. |
| src/components/rich-text-input/minimal-tiptap/extensions/image/components/image-actions.tsx | Updated menu/tooltip triggers to Base UI render pattern + lucide icons. |
| src/components/rich-text-input/minimal-tiptap/components/toolbar-section.tsx | Updated dropdown trigger to Base UI render pattern + lucide icon. |
| src/components/rich-text-input/minimal-tiptap/components/toolbar-button.tsx | Updated tooltip trigger typing/usage to Base UI render pattern. |
| src/components/rich-text-input/minimal-tiptap/components/section/two.tsx | Replaced Radix icons with lucide icons. |
| src/components/rich-text-input/minimal-tiptap/components/section/three.tsx | Updated tooltip/popover triggers + toggle group usage for Base UI. |
| src/components/rich-text-input/minimal-tiptap/components/section/one.tsx | Updated dropdown trigger + icons for Base UI. |
| src/components/rich-text-input/minimal-tiptap/components/section/four.tsx | Updated icons for Base UI. |
| src/components/rich-text-input/minimal-tiptap/components/section/five.tsx | Updated icons for Base UI. |
| src/components/rich-text-input/minimal-tiptap/components/link/link-popover-block.tsx | Replaced Radix icons with lucide icons. |
| src/components/rich-text-input/minimal-tiptap/components/link/link-edit-popover.tsx | Updated popover trigger to Base UI render pattern + lucide icon. |
| src/components/rich-text-input/minimal-tiptap/components/image/image-edit-dialog.tsx | Updated dialog trigger to Base UI render pattern + lucide icon. |
| src/components/admin/user-menu.tsx | Updated dropdown trigger/content patterns for Base UI Menu. |
| src/components/admin/theme-mode-toggle.tsx | Updated dropdown trigger to Base UI render pattern. |
| src/components/admin/sort-button.tsx | Updated nested dropdown/tooltip trigger composition to Base UI render pattern. |
| src/components/admin/simple-form-iterator.tsx | Updated tooltip triggers to Base UI render pattern. |
| src/components/admin/radio-button-group-input.tsx | Adjusted prop typing exclusions. |
| src/components/admin/locales-menu-button.tsx | Updated dropdown trigger to Base UI render pattern. |
| src/components/admin/icon-button-with-tooltip.tsx | Updated tooltip trigger to Base UI render pattern. |
| src/components/admin/form.tsx | Removed Radix Slot/Label usage; reworked FormControl cloning behavior. |
| src/components/admin/filter-form.tsx | Updated dropdown trigger to Base UI render pattern. |
| src/components/admin/error.tsx | Updated accordion prop usage for Base UI. |
| src/components/admin/data-table.tsx | Updated tooltip trigger to Base UI render pattern. |
| src/components/admin/columns-button.tsx | Migrated popover implementation to Base UI Popover + portal usage changes. |
| src/components/admin/bulk-export-button.tsx | Tightened onClick event typing. |
| src/components/admin/breadcrumb.tsx | Updated drawer close button styling approach. |
| src/components/admin/boolean-input.tsx | Updated Switch onFocus typing. |
| src/components/admin/autocomplete-input.tsx | Updated popover typing/imports for Base UI Popover. |
| src/components/admin/authentication.tsx | Updated Button-as-link to Base UI render pattern. |
| src/components/admin/app-sidebar.tsx | Updated SidebarMenuButton to Base UI render pattern. |
| scripts/build_registry.mjs | Updated shadcn CLI command for v4. |
| registry.json | Updated registry dependencies/files list for new structure. |
| package.json | Removed Radix deps; added Base UI + updated shadcn version. |
| package-test.json | Updated shadcn version used in tests. |
| eslint.config.js | Expanded ignored build output directories. |
| docs/src/content/docs/AppSidebar.md | Updated docs examples to render pattern. |
| components.json | Updated shadcn config style + new config fields. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
src/components/ui/sidebar.tsx:490
- These Tailwind classes use a trailing
!(e.g.size-8!,p-2!,p-0!). Tailwind's important modifier is a!prefix, so these will be ignored and the collapsed/icon sizing overrides likely won't work.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| <a | ||
| href="https://github.com/marmelab/shadcn-admin-kit" | ||
| target="_blank" | ||
| /> | ||
| } |
There was a problem hiding this comment.
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse-tabnabbing and to avoid leaking window.opener. Please add rel on this anchor.
website/src/components/Header.tsx
Outdated
| <img | ||
| src={GithubLogo} | ||
| alt="Github" | ||
| className="inline h-4 w-auto" | ||
| />{" "} | ||
| Star us on Github! |
There was a problem hiding this comment.
Brand spelling: the GitHub name should be capitalized as "GitHub" (alt text + visible label).
| <Button | ||
| className="mt-10" | ||
| size="lg" | ||
| variant="outline" | ||
| render={<a href="https://marmelab.com/shadcn-admin-kit/docs" target="_blank" />} | ||
| nativeButton={false} | ||
| > |
There was a problem hiding this comment.
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse-tabnabbing and to avoid leaking window.opener. Please add rel on this anchor.
| {children} | ||
| <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-primary fill-primary data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" /> | ||
| </TooltipPrimitive.Popup> |
There was a problem hiding this comment.
The class list uses top-1/2! (and similar) which is not valid Tailwind syntax for the important modifier. Tailwind expects !top-1/2; as-is these positioning rules for the arrow won't be generated/applied.
| return ( | ||
| <div | ||
| data-slot="command-input-wrapper" | ||
| className="flex h-9 items-center gap-2 border-b px-3" | ||
| > | ||
| <SearchIcon className="size-4 shrink-0 opacity-50" /> | ||
| <CommandPrimitive.Input | ||
| data-slot="command-input" | ||
| className={cn( | ||
| "placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| <div data-slot="command-input-wrapper" className="p-1 pb-0"> | ||
| <InputGroup className="h-8! rounded-lg! border-input/30 bg-input/30 shadow-none! *:data-[slot=input-group-addon]:pl-2!"> | ||
| <CommandPrimitive.Input |
There was a problem hiding this comment.
Several classes use a trailing ! (e.g. h-8!, rounded-xl!, shadow-none!). Tailwind's important modifier is a ! prefix (e.g. !h-8), so these will be ignored and the intended overrides won't apply.
src/components/ui/separator.tsx
Outdated
| orientation = "horizontal", | ||
| decorative = true, | ||
| decorative = false, | ||
| ...props | ||
| }: React.ComponentProps<typeof SeparatorPrimitive.Root>) { | ||
| }: SeparatorPrimitive.Props & { decorative?: boolean }) { |
There was a problem hiding this comment.
decorative now defaults to false, which makes separators exposed to assistive tech by default. In most UI usages separators are decorative; consider defaulting back to true (or making callers opt into non-decorative separators) to avoid accessibility noise/regressions.
| const resolveContainer = () => { | ||
| const target = document.getElementById(elementId); | ||
| if (target) { | ||
| setContainer(target); | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| }; | ||
|
|
||
| if (typeof document === "undefined" || resolveContainer()) { | ||
| return; | ||
| } | ||
|
|
||
| // look for the container in the DOM every 100ms | ||
| const interval = setInterval(() => { | ||
| const target = document.getElementById(elementId); | ||
| if (target) setContainer(target); | ||
| if (resolveContainer()) { | ||
| clearInterval(interval); | ||
| } | ||
| }, 100); |
There was a problem hiding this comment.
The DOM polling setInterval has no upper bound now; if the target element never appears, this interval will run indefinitely while the component stays mounted. Please add a timeout/max-attempts (or switch to a MutationObserver) so this can't leak work in edge cases.
| size="default" | ||
| className={cn("gap-1 px-2.5 sm:pl-2.5", className)} | ||
| className={cn("pl-2!", className)} | ||
| {...props} |
There was a problem hiding this comment.
Tailwind's important modifier uses a ! prefix (e.g. !pl-2), not a suffix. pl-2! will be ignored, so the left padding override likely won't apply.
| size="default" | ||
| className={cn("gap-1 px-2.5 sm:pr-2.5", className)} | ||
| className={cn("pr-2!", className)} | ||
| {...props} |
There was a problem hiding this comment.
Tailwind's important modifier uses a ! prefix (e.g. !pr-2), not a suffix. pr-2! will be ignored, so the right padding override likely won't apply.
| <a | ||
| href="https://marmelab.com/shadcn-admin-kit/docs/install/" | ||
| target="_blank" | ||
| /> | ||
| } |
There was a problem hiding this comment.
Links opened with target="_blank" should include rel="noopener noreferrer" to prevent reverse-tabnabbing and to avoid leaking window.opener. Please add rel on this anchor (and any similar ones in this component).
slax57
left a comment
There was a problem hiding this comment.
Lastly, I can still find references to radix in:
README.mdSKILLS.mdselect-input.tsx(in a comment)Technos.tsx(website)
Other than that, I tested several components, including the demo, storybook etc. and they all looked well! Great job!
| <SidebarMenuItem> | ||
| <SidebarMenuButton | ||
| asChild | ||
| render={<Link to="/" />} |
There was a problem hiding this comment.
This is a clue some changes might be needed at the user-level too. So IMO this new feature deserves to be released in a new major version.
Besides, we need to document the migration to Base UI IMO. We can for instance link to shadcn-ui/ui#9562 since there seems to be no official migration guide (see shadcn-ui/ui#9142).
| // stop looking after 500ms | ||
| const timeout = setTimeout(() => clearInterval(interval), 500); |
There was a problem hiding this comment.
FMI why did you remove that? 🤔
There was a problem hiding this comment.
After the Base UI migration, the columns menu stop appearing. I changed the implementation to fix that regression, and then adjusted it again after Copilot's review to keep the fix while adding a bounded stop condition for the polling.
package-test.json
Outdated
| "eslint-plugin-react-refresh": "^0.4.19", | ||
| "globals": "^16.0.0", | ||
| "shadcn": "^2.5.0", | ||
| "shadcn": "4.0.3", |
There was a problem hiding this comment.
| "shadcn": "4.0.3", | |
| "shadcn": "^4.0.3", |
package.json
Outdated
| "ra-data-json-server": "^5.13.6", | ||
| "react-error-boundary": "^6.1.0", | ||
| "shadcn": "3.8.5", | ||
| "shadcn": "4.0.3", |
There was a problem hiding this comment.
| "shadcn": "4.0.3", | |
| "shadcn": "^4.0.3", |
There was a problem hiding this comment.
I still see some radix packages in that file. Is that to be expected? 🤔
There was a problem hiding this comment.
Yes, this is expected.
pnpm-lock.yaml still contains some transitive Radix packages coming from third-party dependencies, mainly cmdk and vaul.
registry.json
Outdated
| { | ||
| "path": "src/components/admin/confirm.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/admin/guesser-empty.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/admin/icon-button-with-tooltip.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/admin/select-all-button.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/admin/spinner.tsx", | ||
| "type": "registry:component" | ||
| }, |
registry.json
Outdated
| { | ||
| "path": "src/components/ui/accordion.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/ui/alert.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/ui/avatar.tsx", | ||
| "type": "registry:component" | ||
| }, | ||
| { | ||
| "path": "src/components/ui/badge.tsx", | ||
| "type": "registry:component" | ||
| }, |
There was a problem hiding this comment.
Why add shadcn/ui components as part of our registry? I don't understand... 🤔
There was a problem hiding this comment.
This became necessary after the Base UI migration.
These blocks rely on the local wrappers used by shadcn-admin-kit, not only on generic shadcn/ui components. Without that, shadcn add was pulling mismatched implementations and the generated app was failing to build.
There was a problem hiding this comment.
If these become shadcn-admin-kit wrappers, then I'd advocate in favor of moving them under src/components/admin, to make it clear they now belong to the kit and need to be maintained by us. Nevertheless I'm still surprised there are no components we can put as registryDependencies. How do we know what components come from shadcn/ui and what components were written by us?
There was a problem hiding this comment.
Yes, I agree this was too aggressive a strategy. I’ll try to revert it.
|
|
||
| For example, to customize the app name and logo, edit the `SidebarHeader` section: | ||
|
|
||
| ```tsx {2,22-25} |
There was a problem hiding this comment.
highlighted lines need to be updated accordingly
| <DropdownMenuItem | ||
| className="m-1" | ||
| render={ | ||
| <a | ||
| href="https://marmelab.com/shadcn-admin-kit/demo" | ||
| target="_blank" | ||
| /> | ||
| } |
There was a problem hiding this comment.
This looks like a project-level regression in the Reviews status filter rather than a generic AutocompleteInput issue, so I fixed it at the usage site to restore the previous behavior without making the shared component more opinionated.



Ref: #104
Reinstall
shadcn/uiwithBase UIinstead ofRadix-uiRadix UItoBase UIshadcnto version 4 and regenerated the core UI wrappersBase UIpatterns directly@radix-ui/*dependencies from the repoHow to Test
pnpm devand test the main demo locally, including menus, popovers, columns, forms, and the rich text input demo route.pnpm website:devand check the website locally, especially header navigation, dropdowns, and tooltips.pnpm storybookand review the updated component stories, especially Base UI wrappers and the rich text input.