Migration from shadcn/Radix
Kernel covers most of the same UI surface as shadcn/ui, but the APIs are not drop-in replacements. Many components are structurallydifferent because Kernel wraps real native elements (<dialog>,<select>, <details>) instead of Radix's compound component trees.
Start with kernel init and kernel doctor — see the CLI guide. Then migrate screen by screen rather than attempting a whole-repo rewrite.
What changes everywhere
asChild→render— polymorphic composition uses arenderprop, not Radix Slot.- Required labels — form fields need a
labelprop; usehideLabelwhen the label should be visually hidden. - No copied source — you install packages and import components; there is no
components/ui/folder to own. - CSS, not a theme provider — theming is CSS custom properties from
@kernelui-lib/styles, not a React context.
<Button render={<a href="/docs">Docs</a>}>
Read the docs
</Button>Component mapping
The table below is generated from the shared registry. Runkernel docs <shadcn-name> for details on any row.
| Kernel | shadcn alias | React export |
|---|---|---|
| Accordion | accordion, collapsible | Accordion |
| Avatar | avatar | Avatar |
| Badge | badge | Badge |
| Breadcrumbs | breadcrumb | Breadcrumbs |
| Button | button | Button |
| Callout | alert | Callout |
| Card | card | Card |
| Carousel | carousel | Carousel |
| Checkbox | checkbox | Checkbox |
| Combobox | combobox, command | Combobox |
| Command Palette | command | CommandPalette |
| Context Menu | context-menu | ContextMenu |
| Data Table | data-table, table | DataTable |
| Date Picker | calendar, date-picker | DatePicker |
| Date Range Picker | date-range-picker | DateRangePicker |
| Dialog | dialog, alert-dialog | Dialog |
| Dropdown Menu | dropdown-menu, menubar | DropdownMenu |
| Hover Card | hover-card | HoverCard |
| Input OTP | input-otp | InputOTP |
| Label | label | Label |
| Navigation Menu | navigation-menu | NavigationMenu |
| Pagination | pagination | Pagination |
| Popover | popover | Popover |
| Progress | progress | Progress |
| Radio Group | radio-group | RadioGroup |
| Resizable | resizable | Resizable |
| Scroll Area | scroll-area | ScrollArea |
| Select | select | Select |
| Separator | separator | Separator |
| Sheet | sheet, drawer | Sheet |
| Sidebar | sidebar | Sidebar |
| Skeleton | skeleton | Skeleton |
| Slider | slider | Slider |
| Switch | switch | Switch |
| Table | table | Table |
| Tabs | tabs | Tabs |
| Text Field | input | TextField |
| Textarea | textarea | Textarea |
| Toast | toast, sonner | ToastViewport |
| Toggle | toggle, toggle-group | Toggle |
| Tooltip | tooltip | Tooltip |
Structural differences to plan for
Accordion
- Radix Accordion uses controlled value/onValueChange on the root; Kernel AccordionItem uses title + defaultOpen per item.
- No Radix-style AccordionTrigger/AccordionContent composition — pass title and children to AccordionItem.
Button
- shadcn asChild maps to Kernel's render prop: <Button render={<a href="..." />}>.
Checkbox
- onCheckedChange naming matches Radix; value API uses checked/defaultChecked/onCheckedChange.
Combobox
- Custom Radix/shadcn Select with search/filter usually maps here, not native Select.
- Options are passed as an options prop array, not SelectItem children.
Data Table
- TanStack Table column definitions differ; Kernel DataTable takes a columns prop with a simpler API.
Dialog
- Kernel Dialog is a single component with title/description/children props — not Dialog.Root/Trigger/Portal/Content.
- shadcn Alert Dialog is this component with closeOnBackdropClick={false}. shadcn Sheet is Kernel Sheet, which adds drag-to-dismiss on top of this.
Dropdown Menu
- Use render prop instead of asChild on the trigger: <DropdownMenu render={<Button />}>.
Popover
- Use render prop on the trigger instead of asChild.
Select
- Kernel Select wraps a native <select> — the OS renders the picker.
- Custom portal/listbox Select from Radix usually maps to Combobox instead.
- Use SelectOption (or real <option> in elements) instead of SelectItem.
Sheet
- Replaces Vaul: Drawer.Root/Trigger/Portal/Overlay/Content/Handle collapse into one <Sheet> with open/onOpenChange, since a real <dialog> needs no portal and draws its own ::backdrop.
- Vaul's snapPoints becomes snapPoints (viewport percentages) and activeSnapPoint becomes snap/defaultSnap/onSnapChange; snapping works on every side, as a percentage of the axis the sheet grows along.
- direction becomes side; dismissible and handleOnly keep their names; closeThreshold keeps Vaul's 0.25, but velocityThreshold is 0.5 because release speed is measured over a window rather than the whole gesture.
Switch
- onCheckedChange naming matches Radix.
Tabs
- value/onValueChange API matches Radix; subcomponent names differ (Tab vs TabsTrigger).
Text Field
- label is required — use hideLabel for visually hidden labels.
- shadcn Input without a label needs a TextField with label + hideLabel.
Textarea
- label is required — same form-field scaffold as TextField.
Toast
- Mount <ToastViewport /> once, then call toast() from anywhere — similar to Sonner.
Tooltip
- Use render prop on the trigger instead of asChild.
Common patterns
Input → TextField
shadcn's unlabeled Input becomes a TextField with a required label:
<TextField
label="Email"
hideLabel
type="email"
placeholder="you@example.com"
/>Dialog / Sheet / Alert Dialog
All collapse into one Dialog component withtitle, description, and childrenprops — not a Root/Trigger/Portal/Content tree.
<Dialog
open={open}
onOpenChange={setOpen}
title="Delete project"
description="This cannot be undone."
>
<p>Project files will be permanently removed.</p>
</Dialog>Select vs Combobox
- Select — native
<select>; the OS renders the picker. Use when a platform-native picker is acceptable. - Combobox — custom filterable listbox. Use when migrating a styled Radix Select with search or custom item rendering.
Incremental adoption
- Run
kernel initin a branch. - Pick one screen or feature to migrate.
- Start with mechanical swaps: Button, Badge, Separator, Checkbox, Switch.
- Tackle structural components (Dialog, Select, Accordion) with the caveats above.
- Use headless mode (tokens only) on pages you have not migrated yet.