Skip to content
General

Migrate from shadcn/ui

WunderUI is built the way a shadcn/ui project already works, so most of your setup carries over. This page shows what stays, what changes, and how to move one page at a time while both libraries run side by side.

At a glance

What you can keep:

SharedIn WunderUI
Source you ownThe 83 free components are MIT source. writes them into , like the shadcn CLI.
Tailwind CSS v4CSS-first setup with @theme, no tailwind.config.js.
Base UIThe primitives come from , so the Base UI flavour of shadcn/ui is the closest match.
cn()The same helper name in , taught WunderUI's own utilities (see Pitfalls).
data-slotEvery part carries , so selectors such as keep working.
CSS variablesThe shadcn/ui token names exist with the same meaning, light under and dark under .
lucide-reactThe icon set the components use.

What is different:

AreaIn WunderUI
TokensMore of them: text levels, link, control borders, tints, a brand scale. See Theming.
DensityControl heights, row padding and card padding are tokens with a compact default and a comfortable mode.
Design sourceA Figma library with the same names, variants and tokens as the code.
MotionNamed duration and easing tokens (, ) and rules for what may move. See Motion.
AgentsDESIGN.md, design.json, an MCP server and a CLI that answers questions offline. See Build with AI.
ScopeCharts, data tables, AI and agent components in Core and Pro; whole screens as UI Blocks with Pro.

1. Install WunderUI Free

Two ways in. The CLI writes components as source, exactly like the shadcn CLI does. The package keeps them in , which is the easier route while both libraries live in one app.

bash
npx wunderui-cli add button card dialog

Or install the Free package:

bash
npm install @wunderui/react

The npm package is WunderUI Free and holds the free components only. Core and Pro come from a private repository; Installation shows how.

2. Wire up the styles

Import WunderUI after Tailwind and point at the components. With the CLI that is the files it wrote; with the package, its folder:

css
/* app/globals.css */
@import "tailwindcss";
@import "tw-animate-css";
@import "@wunderui/react/styles.css";      /* CLI: "../styles/wunderui.css" */
@source "../node_modules/@wunderui/react/dist"; /* CLI: "../components" */

/* your shadcn/ui :root, .dark and @theme inline blocks follow here */

3. Map your theme

Every shadcn/ui variable has a WunderUI counterpart under the same name, so a theme you wrote for shadcn/ui keeps working. The values and a few roles differ:

shadcn/ui variableWunderUI
Same names. Layouts put cards on --bg-page, a tinted canvas in light mode.
Same names and roles.
Same names. Primary reads from the brand scale --brand-50 … --brand-1000 (indigo by default); change the scale to rebrand.
Same names, equal to the card colour. The plain button uses --card and --border-control.
Same names. Two more text levels: --text-secondary and --text-tertiary.
Same names, equal to muted.
Same name, alias --error. --destructive-foreground is kept.
Same name, for dividers and card outlines. Controls use --border-control.
Same name. Light: equal to --border-control; dark: a darker grey at 3:1.
Same name, drawn as a 1 px focus line in the brand colour.
Same name, 0.75rem. Only rounded-3xl and rounded-4xl derive from it (see Pitfalls).
All five, --chart-1 to --chart-5: brand indigo first, then cool neighbours, grey last.
Same eight names, including --sidebar-ring.

New on top: , , , the text tones , and , nine fills, and the density and motion tokens. The Theme Builder writes a brand theme from one colour.

4. Map your components

The shadcn/ui components and their WunderUI counterparts. Notes cover the free components; Core and Pro components are listed by name.

shadcn/ui → WunderUIWhat changes
AccordionAccordionFreeSame idea and parts.
AlertAlertFree is a tone: plain (default), primary, positive, negative, warning; plus small or large.
Alert DialogAlert DialogFreeSame parts, plus .
Aspect RatioNo counterpartNo component. Use Tailwind's or .
AvatarAvatarFreeAvatar GroupFreeSizes xs to xl, circle or square, a for the fallback.
BadgeBadgeFreeNo : pick a (12, default blue) and a (plain, light, ghost and three border styles).
BreadcrumbBreadcrumbFreeSame idea and parts.
ButtonButtonFreeIcon ButtonFree becomes , becomes ; outline, ghost, destructive and link keep their names. Sizes run sm to 2xl (default md). Icon-only buttons are , not .
Button GroupButton GroupFreeSame idea and parts.
CalendarCalendarFreeBuilt in, without react-day-picker, so its props differ.
CardCardFreeSame parts plus ; 16 px corners and padding from the density tokens.
CarouselCarouselFreeBuilt in, without Embla.
ChartArea ChartCore & ProBar ChartCore & ProLine ChartCore & ProPie ChartCore & ProIncluded in Core and Pro.
CheckboxCheckboxFreeSame idea and parts.
CollapsibleCollapsibleFreeSame idea and parts.
ComboboxComboboxFreeSame idea and parts.
CommandCommandCore & ProIncluded in Core and Pro.
Context MenuContext MenuCore & ProIncluded in Core and Pro.
Data TableData TableCore & ProData GridCore & ProIncluded in Core and Pro.
Date PickerDate PickerFreeOne component (a Calendar in a Popover with Cancel and Apply), not a recipe to assemble.
DialogDialogFreeSame parts, plus .
DrawerSheetFreeNo Vaul drawer: use with .
Dropdown MenuDropdown MenuCore & ProIncluded in Core and Pro.
EmptyEmpty StateFreeOne component, , with a (default dashed) instead of composed parts.
FieldFieldFreeFieldsetFree, , , on Base UI Field; groups use .
Hover CardHover CardFreeSame idea and parts.
InputInputFreeHeight comes from the density tokens: 36 px by default.
Input GroupInput GroupFreeTwo parts: and .
Input OTPOTP FieldFree and on Base UI, without the input-otp package.
ItemItem CardFreeSelector ItemFree for a row card; for a row with a radio, checkbox or switch.
KbdKbdFreeSame idea and parts.
LabelLabelFreeSame idea and parts.
MenubarNo counterpartNo equivalent. Toolbar groups buttons and links.
Native SelectNo counterpartNo styled native . Use Native Select (see Select).
Navigation MenuNavbarCore & ProIncluded in Core and Pro.
PaginationPaginationFreeAlso for previous and next only.
PopoverPopoverFreeSame idea and parts.
ProgressProgressFreeSame idea and parts.
Radio GroupRadio GroupFreeRadio Button GroupFreeSame idea and parts.
ResizableResizableCore & ProIncluded in Core and Pro.
Scroll AreaScroll AreaCore & ProIncluded in Core and Pro.
SelectNative SelectFreeInline SelectFreeDespite its name, is a Base UI Select with a styled popup. sits inside a sentence.
SeparatorSeparatorFreeDivider with LabelFreeSame idea and parts.
SheetSheetFree is right (default), left or bottom.
SidebarSidebarCore & ProIncluded in Core and Pro.
SkeletonSkeletonFreeA for title, text, avatar or block.
SliderSliderFreeSame idea and parts.
Sonner / ToastToastFreeBase UI Toast instead of Sonner: wrap the app in and call .
SpinnerSpinnerFreeSame idea and parts.
SwitchSwitchFreeSame idea and parts.
TableTableFreeSame parts plus ready-made cell types. Sorting and filtering live in Data Table.
TabsTabsFreeAdds .
TextareaTextareaFreeSame idea and parts.
ToggleToggleFreeSame idea and parts.
Toggle GroupRadio Button GroupFreeCheckbox Button GroupFreeTwo components: one choice or several.
TooltipTooltipFreeSame idea and parts.
TypographyNo counterpartNo component. See Typography for the text styles.

5. Migrate step by step

  1. Run both. Install the package and keep your folder. The import path tells them apart; alias where a file needs both:
tsx
import { Button } from "@/components/ui/button"           // shadcn/ui
import { Button as WButton, Card } from "@wunderui/react" // WunderUI
  1. Keep one theme. While your token blocks stay in , both libraries share your shadcn/ui colours, so mixed pages look consistent.
  2. Switch page by page. Change the imports of one route, adjust the props from the table above ( → , → ), check it in light and dark, then move on.
  3. Remove what is unused. When nothing imports a shadcn/ui file any more, delete it, and its dependencies with it (for example , or once nothing uses them).
  4. Hand over the theme. Delete your own , and blocks, or keep only the brand values you want on top. Your Tailwind setup, and the class switch stay as they are.

6. Pitfalls

Watch out forWhy
Focus and bordersFocus is a 1 px line (an outline on buttons, the border on fields), not a wide translucent ring. Control rims use ; is the lighter divider grey.
Radius to are fixed: 4, 8, 12, 16 and 24 px. Cards use 16 px. Changing only moves the two largest steps.
DensityButtons and fields default to the compact scale (36 px at size md). Set for the roomier sizes; see Density.
Motion is still loaded, so your classes work. New code uses the tokens: to and , , .
16 px fields on phonesOn touch screens and below 640 px, inputs, selects and textareas render at 16 px so iOS Safari does not zoom on focus.
Palette namesWunderUI redefines Tailwind's blue, indigo, purple, pink, red, orange, yellow and green with its own 100–1000 scales, so changes colour after the import.
cn() in lib/utils.tsWunderUI's knows its own utilities (, , , gradients). The CLI does not replace an existing ; swap it so class merging stays correct.
Dark modeThe same class, for example from next-themes with . WunderUI's variant also respects a nested section; a later in your file replaces it.
Tailwind versionTailwind v4 is required; there is no v3 build.
Iconslucide-react, as in shadcn/ui. Keep one icon set per surface.

7. Let your agent help

Give your coding agent the WunderUI context before it rewrites imports, so it uses the real props instead of the shadcn/ui ones it remembers:

bash
claude mcp add wunderui -- npx -y wunderui-mcp
npx skills add wunder-ui/wunderui

The MCP server answers component and token questions while the agent works, and DESIGN.md carries the rules in one file.

InstallationThe full setup, including Core and Pro.
ThemingBrand colour, dark mode, radius, fonts and density.