Migrating from Radix

A comprehensive guide for migrating from Radix-based libraries to coss ui components.

This guide is for developers who already have applications built on Radix-based libraries — frameworks that wrap Radix primitives — and want to adopt coss ui.

coss ui is built on top of Base UI, using a custom abstraction that stays intentionally close to its mental model. The component API exposed by coss ui is intentionally similar to shadcn/ui, which is why this guide also highlights the main differences between shadcn/ui and coss ui.

Overview

coss ui uses Base UI internally, not Radix. This means the architecture and API patterns are different by design. However, we've worked to make the migration path as clear as possible by:

  • Providing detailed component-by-component migration guides below
  • Maintaining similar component names and structures where it makes sense
  • Offering clear prop mappings and code examples

Tip: This migration guide is structured to be LLM-friendly. You can provide this page to AI coding assistants (like Claude, ChatGPT, etc.) to help automate the conversion of your Radix components to coss ui.

General Migration Patterns

The asChild to render Pattern

The most common change across all components is replacing Radix UI's asChild prop with Base UI's render prop:

Radix
coss ui

Component Naming Conventions

Some components have updated names for clarity and consistency:

  • *Content*Popup or *Panel (e.g., DialogContentDialogPopup)
  • Legacy names are often kept for backward compatibility

Component Migration Guides

Accordion

Quick Checklist:

  • Replace type="multiple"multiple={true} on Accordion
  • Remove type="single" from Accordion
  • Remove collapsible from Accordion
  • Always use arrays for defaultValue
  • Use AccordionPanel going forward; AccordionContent remains for legacy
  • If you used asChild on parts, switch to the render prop

Prop Mapping:

Comparison Example:

shadcn/ui
coss ui

Alert

New Variants:

We've added new colored variants for better semantic meaning:

Ensure you have the following variables imported in your CSS file:

  • --destructive-foreground
  • --info
  • --info-foreground
  • --success
  • --success-foreground
  • --warning
  • --warning-foreground

Alert Dialog

Quick Checklist:

  • Replace asChildrender on AlertDialogTrigger and closing buttons
  • Replace AlertDialogAction and AlertDialogCancelAlertDialogClose
  • Prefer AlertDialogPopup; AlertDialogContent remains for legacy
  • Use AlertDialogPanel to wrap main content between AlertDialogHeader and AlertDialogFooter
  • If you used asChild on any other parts, switch to the render prop

Comparison Example:

shadcn/ui
coss ui

Avatar

Quick Checklist:

  • Replace asChildrender on Avatar

Comparison Example:

shadcn/ui
coss ui

Badge

Quick Checklist:

  • Replace asChildrender on Badge

Size Comparison:

Compared to shadcn/ui, our Badge component includes size variants for better density control. shadcn/ui badges have a fixed size, while our component offers flexible sizing with sm, default, and lg options.

So, if you want to preserve the original shadcn/ui badge size, you should use the lg size in coss ui.

New Variants:

We've added new colored variants to the existing ones (default, destructive, outline, secondary) for better semantic meaning and visual communication:

Ensure you have the following variables imported in your CSS file:

  • --destructive-foreground
  • --info
  • --info-foreground
  • --success
  • --success-foreground
  • --warning
  • --warning-foreground

Comparison Example:

shadcn/ui
coss ui

Button

Quick Checklist:

  • Replace asChildrender on Button

Size Comparison:

coss ui button sizes are more compact compared to shadcn/ui, making them better suited for dense applications. We also introduce new sizes (xs, xl, icon-sm, icon-lg) for more granular control:

So, for example, if you were using the default size in shadcn/ui and you want to preserve the original height, you should use the lg size in coss ui.

New Variants:

We've added a new destructive-outline variant for better UX patterns:

  • Primary actions: Use destructive (solid red) for the main destructive action
  • Secondary triggers: Use destructive-outline (outline red) to avoid alarming red buttons in the main interface

Comparison Example:

shadcn/ui
coss ui

Card

Quick Checklist:

  • Use CardPanel going forward; CardContent remains for legacy

Checkbox

Quick Checklist:

  • Replace asChildrender on Checkbox

Collapsible

Quick Checklist:

  • Replace asChildrender on CollapsibleTrigger
  • Prefer CollapsiblePanel; CollapsibleContent remains for legacy

Comparison Example:

shadcn/ui
coss ui

Command

The API is significantly different from shadcn/ui (cmdk). Please review both docs before migrating: cmdk Docs and shadcn/ui Command, and our Base UI Autocomplete docs.

Key Differences:

  • No cmdk dependency - built entirely with Base UI's Autocomplete and Dialog components
  • Data-driven approach - pass an items array to Command and use render functions instead of manually composing CommandItem children
  • Use CommandCollection within CommandGroup when rendering grouped data with the items pattern
  • Use CommandDialog, CommandDialogTrigger, and CommandDialogPopup for dialog functionality instead of composing separate Dialog components
  • CommandGroup uses <CommandGroupLabel> as a child instead of a heading prop

Dialog

Quick Checklist:

  • Replace asChildrender on DialogTrigger and closing buttons
  • Prefer DialogPopup; DialogContent remains for legacy
  • Use DialogPanel to wrap main content between DialogHeader and DialogFooter
  • If you used asChild on any other parts, switch to the render prop

Comparison Example:

shadcn/ui
coss ui

Group (Button Group)

Quick Checklist:

  • Prefer Group* component names; ButtonGroup* remain for compatibility
  • GroupSeparator is always required between controls, including outline buttons (unlike shadcn where separators are optional for outline buttons). This ensures consistent focus state handling and better accessibility
  • If you used asChild on ButtonGroupText, switch to the render prop for custom components

Input

Compared to shadcn/ui, our Input component includes size variants for better density control. shadcn/ui inputs have a fixed height of 36px, while our component offers flexible sizing with sm (28px), default (32px), and lg (36px) options.

So, if you want to preserve the original shadcn/ui input height (36px), you should use the lg size in coss ui.

Input Group

Quick Checklist:

  • No InputGroupButton component - use the regular Button component directly inside InputGroupAddon instead
  • To disable an input group, disable the InputGroupInput or InputGroupTextarea directly (and any Button inside it) - no need to add a data-disabled attribute on InputGroup.

Prop Mapping:

Quick Checklist:

  • Replace asChildrender on MenuTrigger and MenuItem
  • Replace onSelectonClick on menu items
  • Update import path from @/components/ui/dropdown-menu@/components/ui/menu
  • Prefer Menu* component names; DropdownMenu* remain for legacy
  • Prefer MenuGroupLabel instead of DropdownMenuLabel
  • Prefer MenuPopup instead of DropdownMenuContent
  • Prefer MenuSubPopup instead of DropdownMenuSubContent
  • If you used asChild on any other parts, switch to the render prop

Comparison Example:

shadcn/ui
coss ui

Popover

Quick Checklist:

  • Replace asChildrender on PopoverTrigger and closing buttons
  • Prefer PopoverPopup; PopoverContent remains for legacy
  • If you used asChild on any other parts, switch to the render prop

Additional Notes:

Base UI introduces PopoverTitle and PopoverDescription to structure headings and helper text inside the popup. Base UI also introduces a PopoverClose component for adding close buttons to the popup.

Comparison Example:

shadcn/ui
coss ui

Preview Card

Quick Checklist:

  • Update import path from @/components/ui/hover-card@/components/ui/preview-card
  • Prefer PreviewCard* component names; HoverCard* remain for legacy
  • Prefer PreviewCardPopup instead of HoverCardContent
  • If you used asChild on parts, switch to the render prop

Comparison Example:

shadcn/ui
coss ui

Progress

Quick Checklist:

  • Prefer ProgressLabel and ProgressValue for label/value instead of inline elements
  • If you render children inside Progress, you must include ProgressTrack and ProgressIndicator (otherwise the bar will not display). Without children, a default bar is rendered for you
  • If you used asChild, switch to the render prop

Additional Notes:

Base UI introduces separate parts — ProgressLabel, ProgressValue, ProgressTrack, and ProgressIndicator — which you compose inside Progress for greater flexibility.

Radio Group

Quick Checklist:

  • Use Radio going forward; RadioGroupItem remains for legacy
  • Replace asChildrender on parts if used

Scroll Area

Quick Checklist:

  • If you used asChild on parts, switch to the render prop

Select

Important: Base UI changes how options are provided. Instead of deriving options from children only (Radix pattern), you should pass an items prop (array or record) so values and labels are known before hydration. This avoids SSR pitfalls and improves mount performance. Alternatively, provide a function child to SelectValue to format the label. See the Base UI Select docs.

Prop Mapping:

Quick Checklist:

  • Set items prop on Select
  • Remove placeholder from Select
  • Prefer SelectPopup instead of SelectContent
  • If you used asChild on parts, switch to the render prop

Size Comparison:

coss ui select sizes are more compact compared to shadcn/ui, making them better suited for dense applications:

So, for example, if you were using the default size in shadcn/ui and you want to preserve the original height, you should use the lg size in coss ui.

Additional Notes:

Base UI introduces the alignItemWithTrigger prop to control whether the SelectContent overlaps the SelectTrigger so the selected item's text is aligned with the trigger's value text.

Comparison Example:

shadcn/ui
coss ui

Sheet

Quick Checklist:

  • Replace asChildrender on SheetTrigger and closing buttons
  • Prefer SheetPopup; SheetContent remains for legacy
  • Use SheetPanel to wrap main content
  • If you used asChild on any other parts, switch to the render prop

Comparison Example:

shadcn/ui
coss ui

Slider

Quick Checklist:

  • coss ui Slider uses Base UI's multiple value approach
  • Always pass values as arrays (e.g., value={[50]} instead of value={50})
  • onValueChange receives an array of numbers
  • Multiple thumbs are supported natively via array length
  • Replace asChildrender on parts if used

Switch

Quick Checklist:

  • Replace asChildrender on Switch if used

Tabs

Quick Checklist:

  • Replace asChildrender on parts if used
  • Use TabsTab going forward; TabsTrigger remains for legacy
  • Prefer TabsPanel; TabsContent remains for legacy

Additional Notes:

Compared to shadcn/ui, our TabsList component adds variant prop, which allows you to choose between default and underline styles.

Comparison Example:

shadcn/ui
coss ui

Textarea

Compared to shadcn/ui, our Textarea component includes size variants (sm, default, lg) for better density control. For visual consistency, if you're using size="lg" on other form elements like inputs, you should add the same size to textareas as well.

Toast

The API is significantly different from shadcn/ui (Sonner). Please review both docs before migrating: Sonner Docs and shadcn/ui Sonner, and our Base UI toast docs.

Quick Checklist:

  • Replace <Toaster /> component in layout → <ToastProvider> wrapper
  • Toast API calls differ significantly - see comparison below
  • Toast actions use different patterns

Comparison Examples:

shadcn/ui (Sonner)

app/layout.tsx

coss ui (Base UI)

app/layout.tsx

Toggle

Quick Checklist:

  • Replace asChildrender on Toggle if used

Toggle Group

Prop Mapping:

Quick Checklist:

  • Replace type="multiple"multiple on ToggleGroup
  • Remove type="single" from ToggleGroup
  • Always use arrays for defaultValue
  • Use Toggle going forward; ToggleGroupItem remains for legacy
  • Replace asChildrender on parts if used

Size Comparison:

coss ui toggle group sizes are more compact compared to shadcn/ui, making them better suited for dense applications:

So, for example, if you were using the default size in shadcn/ui and you want to preserve the original height, you should use the lg size in coss ui.

Comparison Example:

shadcn/ui
coss ui

Tooltip

Quick Checklist:

  • Replace asChildrender on TooltipTrigger
  • Prefer TooltipPopup; TooltipContent remains for legacy

Comparison Example:

shadcn/ui
coss ui

Additional Resources

Need Help?

If you encounter issues during migration or have questions about specific components, please:

  • Check the individual component documentation pages
  • Review the Base UI documentation for deeper understanding
  • Open an issue on our GitHub repository

Built by and for the team of Wecraft