# Base vs Radix
API differences between `base` and `radix`. Check the `base` field from `npx shadcn@latest info`.
## Contents
- Composition: asChild vs render
- Button / trigger as non-button element
- Select (items prop, placeholder, positioning, multiple, object values)
- ToggleGroup (type vs multiple)
- Slider (scalar vs array)
- Accordion (type and defaultValue)
---
## Composition: asChild (radix) vs render (base)
Radix uses `asChild` to replace the default element. Base uses `render`. Don't wrap triggers in extra elements.
**Incorrect:**
```tsx
Open
```
**Correct (radix):**
```tsx
Open
```
**Correct (base):**
```tsx
}>Open
```
This applies to all trigger and close components: `DialogTrigger`, `SheetTrigger`, `AlertDialogTrigger`, `DropdownMenuTrigger`, `PopoverTrigger`, `TooltipTrigger`, `CollapsibleTrigger`, `DialogClose`, `SheetClose`, `NavigationMenuLink`, `BreadcrumbLink`, `SidebarMenuButton`, `Badge`, `Item`.
---
## Button / trigger as non-button element (base only)
When `render` changes an element to a non-button (``, ``), add `nativeButton={false}`.
**Incorrect (base):** missing `nativeButton={false}`.
```tsx
}>Read the docs
```
**Correct (base):**
```tsx
} nativeButton={false}>
Read the docs
```
**Correct (radix):**
```tsx
Read the docs
```
Same for triggers whose `render` is not a `Button`:
```tsx
// base.
} nativeButton={false}>
Pick date
```
---
## Select
**items prop (base only).** Base requires an `items` prop on the root. Radix uses inline JSX only.
**Incorrect (base):**
```tsx
```
**Correct (base):**
```tsx
const items = [
{ label: "Select a fruit", value: null },
{ label: "Apple", value: "apple" },
{ label: "Banana", value: "banana" },
]
{items.map((item) => (
{item.label}
))}
```
**Correct (radix):**
```tsx
Apple
Banana
```
**Placeholder.** Base uses a `{ value: null }` item in the items array. Radix uses ``.
**Content positioning.** Base uses `alignItemWithTrigger`. Radix uses `position`.
```tsx
// base.
// radix.
```
---
## Select — multiple selection and object values (base only)
Base supports `multiple`, render-function children on `SelectValue`, and object values with `itemToStringValue`. Radix is single-select with string values only.
**Correct (base — multiple selection):**
```tsx
{(value: string[]) => value.length === 0 ? "Select fruits" : `${value.length} selected`}
...
```
**Correct (base — object values):**
```tsx
plan.name}>
{(value) => value.name}
...
```
---
## ToggleGroup
Base uses a `multiple` boolean prop. Radix uses `type="single"` or `type="multiple"`.
**Incorrect (base):**
```tsx
Daily
```
**Correct (base):**
```tsx
// Single (no prop needed), defaultValue is always an array.
Daily
Weekly
// Multi-selection.
Bold
Italic
```
**Correct (radix):**
```tsx
// Single, defaultValue is a string.
Daily
Weekly
// Multi-selection.
Bold
Italic
```
**Controlled single value:**
```tsx
// base — wrap/unwrap arrays.
const [value, setValue] = React.useState("normal")
setValue(v[0])}>
// radix — plain string.
const [value, setValue] = React.useState("normal")
```
---
## Slider
Base accepts a plain number for a single thumb. Radix always requires an array.
**Incorrect (base):**
```tsx
```
**Correct (base):**
```tsx
```
**Correct (radix):**
```tsx
```
Both use arrays for range sliders. Controlled `onValueChange` in base may need a cast:
```tsx
// base.
const [value, setValue] = React.useState([0.3, 0.7])
setValue(v as number[])} />
// radix.
const [value, setValue] = React.useState([0.3, 0.7])
```
---
## Accordion
Radix requires `type="single"` or `type="multiple"` and supports `collapsible`. `defaultValue` is a string. Base uses no `type` prop, uses `multiple` boolean, and `defaultValue` is always an array.
**Incorrect (base):**
```tsx
...
```
**Correct (base):**
```tsx
...
// Multi-select.
...
...
```
**Correct (radix):**
```tsx
...
```