Skip to Content
⭐️ Leave a star →
From Radix UI

Migrating from Radix UI

Radix UI and Wire UI share a philosophy — headless, unstyled, accessible primitives driven by data-* state attributes — so this is one of the smoothest migrations you can make. Most of your styling carries over untouched, because both libraries expose the same [data-state="open"] / [data-disabled] selectors you’re already targeting.

What actually changes:

One package. Radix’s per-primitive packages (@radix-ui/react-dialog, @radix-ui/react-tabs, …) collapse into a single @wire-ui/react.
A few renames. Dialog → Modal, DropdownMenu → Dropdown, Separator → Divider, and a handful more.
Same asChild. Wire UI supports the asChild polymorphism you rely on, so composition patterns transfer directly.
Some overlays restructure. Modal and Dropdown have a slightly different anatomy than their Radix counterparts — a one-time hand edit.

Framework note. Radix UI is React-only. If you’re on Vue you’re likely coming from Reka UI  (the Radix port, formerly Radix Vue); on Solid, from Kobalte or corvu. The component map applies either way, and the Wire UI target code below adapts to the framework you pick above.

1. Swap the dependencies

npm uninstall @radix-ui/react-dialog @radix-ui/react-tabs # …and the rest npm install @wire-ui/react

2. Run the codemod

A jscodeshift codemod does the mechanical 80% — rewriting imports and renaming component namespaces (in both JSX and member expressions):

npx jscodeshift \ -t https://wire-ui.com/codemods/radix-to-wire-ui.cjs \ --extensions=tsx,ts,jsx,js --parser=tsx \ src/

It turns this:

import * as Dialog from '@radix-ui/react-dialog' import * as Tabs from '@radix-ui/react-tabs' import * as Separator from '@radix-ui/react-separator'

into this:

import { Divider, Modal, Tabs } from '@wire-ui/react' // <Dialog.Root> → <Modal.Root>, <Separator.Root> → <Divider.Root>, <Tabs.*> unchanged

What it does: import consolidation + namespace renames — always safe. What it doesn’t: restructure markup, rename parts, or convert props. After running it, let TypeScript guide you to the spots flagged in the map below.

The codemod is for React (jscodeshift). Migrating a Vue (Reka UI) or Solid codebase? The same component map applies — swap the imports to @wire-ui/vue / @wire-ui/solid by hand using your framework’s syntax (toggle the picker on the examples below).

Component map

✅ drop-in · ✏️ codemod renames it · 🔧 needs a hand edit after the codemod

Radix UIWire UINotes
AccordionAccordionSame Root/Item/Trigger/Content. Drop Radix’s Accordion.Header wrapper. type + collapsible props match.
AspectRatioAspectRatioratio prop is identical.
AvatarAvatarRoot/Image/Fallback — identical.
ContextMenuContextMenuRoot/Trigger/Content/Item/Separator.
HoverCardHoverCardRoot/Trigger/Content.
NavigationMenuNavigationMenuRoot/List/Item/Trigger/Content/Link.
PopoverPopoverRoot/Trigger/Portal/Content/Close.
ScrollAreaScrollAreaRoot/Viewport/Scrollbar/Thumb.
SelectSelectRoot/Trigger/Value/Content/Item/Group/GroupLabel/Separator.
SwitchSwitchRoot/Thumb.
TabsTabsRoot/List/Trigger/Content — identical.
ToggleToggleSingle component, pressed prop.
ToggleGroupToggleGroupRoot/Item.
ToolbarToolbarRoot/Button/Toggle/Link/Separator.
MenubarMenuBar✏️Root/Menu/Trigger/Content/Item/Separator/Sub/SubTrigger/SubContent.
SeparatorDivider✏️orientation prop carries over.
FormFormRoot/Field/Label/Control/Message/Submit. See the Form guide.
LabelForm.Label / Input.Label🔧No standalone Label; use the field’s label part.
DialogModal🔧Renamed by the codemod; then restructure (see below).
AlertDialogModal🔧Same as Dialog. Set role="alertdialog" on the content and skip the overlay-click close.
DropdownMenuDropdown🔧Wire UI uses Root/Trigger/Menu; map Radix’s Content/Item into Menu.
RadioGroupRadio🔧Root/Item/Indicator/Label — add Radio.Label per item.
CheckboxCheckbox🔧Wire UI wraps the box in Checkbox.Item; structure is Root/Item/Indicator/Label.
ProgressProgressBar🔧Not compound — a single component driven by a percentage prop (no Indicator).
SliderSlider🔧Not compound — props instead of Track/Range/Thumb.
TooltipTooltip🔧Root/Trigger/Content. Remove Tooltip.Provider; delayDuration is a prop on Tooltip.Root.
CollapsibleAccordion🔧No standalone Collapsible — use Accordion type="single" collapsible with one item.

Components with no Radix equivalent (Calendar, Combobox, Command, FileUpload, the AI primitives, and more) are listed in the API Parity matrix.

Overlays: Dialog → Modal

The one structural change worth walking through. Wire UI’s Modal has no Trigger part (you drive open yourself), nests Content inside Overlay, and uses plain elements for the title/description.

Before (Radix):

import * as Dialog from '@radix-ui/react-dialog' <Dialog.Root> <Dialog.Trigger asChild> <button>Delete account</button> </Dialog.Trigger> <Dialog.Portal> <Dialog.Overlay /> <Dialog.Content> <Dialog.Title>Delete account</Dialog.Title> <Dialog.Description>This cannot be undone.</Dialog.Description> <Dialog.Close asChild><button>Cancel</button></Dialog.Close> </Dialog.Content> </Dialog.Portal> </Dialog.Root>

After (Wire UI):

import { useState } from 'react' import { Modal } from '@wire-ui/react' const [open, setOpen] = useState(false) <button onClick={() => setOpen(true)}>Delete account</button> <Modal.Root open={open} onOpenChange={setOpen}> <Modal.Portal> <Modal.Overlay> <Modal.Content aria-labelledby="title" aria-describedby="desc"> <h2 id="title">Delete account</h2> <p id="desc">This cannot be undone.</p> <Modal.Close>Cancel</Modal.Close> </Modal.Content> </Modal.Overlay> </Modal.Portal> </Modal.Root>

The open / defaultOpen / onOpenChange props are identical to Radix, so only the trigger and the Overlay/Content nesting change. Prefer not to manage state by hand? Use the useDisclosure hook.

After the codemod — checklist

Let tsc run: every 🔧 component surfaces as a type error pointing at the exact line to adjust.
Move Dialog.Trigger / Dialog.Close logic to your own button + open state (or useDisclosure).
Replace Tooltip.Provider wrappers — Wire UI’s Tooltip takes delayDuration on Root.
Your existing CSS keeps working: Wire UI emits the same [data-state] selectors. Confirm against the Data Attributes reference.
Run your axe/keyboard tests — accessibility behavior is equivalent, but verify focus order on the restructured overlays.

Migrating a multi-framework codebase, or coming from another library? See the API Parity matrix and the other migration guides.

Last updated on

MIT License © 2026 wire-ui

Migrating from Radix UI – Wire UI