
shadcn/ui 的 Base 与 Radix 两套组件 API 差异实战render、items 与 defaultValue 的正确写法【免费下载链接】uiA set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.项目地址: https://gitcode.com/GitHub_Trending/ui/uishadcn/ui 仓库在组件注册表中同时维护了基于 Base UIbase与基于 Radix UIradix的两套并行组件实现两者的组合方式asChildvsrender、Select 的数据驱动模型、以及defaultValue的值类型均存在系统性差异。本文以仓库中的 base-vs-radix 规则文档 为主体完整覆盖其全部差异点与正误代码对照并结合 CLI 源码与注册表目录结构说明如何确认当前项目属于哪一套 API从而避免在迁移或跨项目复用组件代码时写出编译不通过或行为不符预期的 JSX。先确认你的项目用的是哪一套npx shadcn info的base字段规则文档的开篇要求先运行npx shadcnlatest info查看输出中的base字段再决定按哪一套 API 编写代码。该字段由 CLI 的 info 命令 生成// packages/shadcn/src/commands/info.ts const config await getConfig(cwd) const base getBase(config?.style)而base的取值逻辑实现在 getBase 函数// packages/shadcn/src/utils/get-config.ts export function getBase(style: string | undefined): PresetBase { // An undefined style means no existing config, so default to base. // Any defined style, including empty and unprefixed legacy values // (new-york, new-york-v4, default), stays radix. if (style undefined) { return base } return parsePresetStyle(style).base ?? radix }从源码结构看可以得出两条实用结论没有components.json配置全新项目时默认归入base任何已定义的 style包括new-york、new-york-v4等遗留值都会停留在radix。也就是说base/radix不是两个可自由混搭的组件库而是同一项目必须整体选定的两套 API。仓库中这两套实现分别位于 apps/v4/registry/bases/base 与 apps/v4/registry/bases/radix 两个并行目录注册表 README 明确要求对任何共享表面同样的预览块、同样的示例意图变更应同时应用到两套变体只调整必须不同的部分——导入路径.../base/ui/...与.../radix/ui/...和原语 API。这正解释了为什么本文列出的每一条差异都必须“成对记忆”同名的DialogTrigger、Select、Accordion在两个目录下的 props 签名并不相同。组合方式radix 用asChildbase 用render两套 API 最基础、也最普遍的区别在于“替换默认元素”的组合机制Radix 使用asChild把默认元素替换为子元素Base 使用renderprop 声明渲染成什么元素。无论哪一套都不要用多余的包裹元素套住 trigger。错误写法两种体系都错DialogTrigger div ButtonOpen/Button /div /DialogTrigger正确写法radixDialogTrigger asChild ButtonOpen/Button /DialogTrigger正确写法baseDialogTrigger render{Button /}Open/DialogTrigger注意两者子内容的语义差异radix 的asChild是“用唯一子元素替换自身”子元素即触发按钮base 的render是“把自身渲染为指定元素”触发文案作为 children 传入。这个差异在 base 注册表的组件源码中随处可见例如 alert-dialog.tsxAlertDialogPrimitive.Cancel >Button render{a href/docs /}Read the docs/Button正确baseButton render{a href/docs /} nativeButton{false} Read the docs /Button等价写法radixButton asChild a href/docsRead the docs/a /Button同样的规则适用于render不是Button的 trigger 组件// base. PopoverTrigger render{InputGroupAddon /} nativeButton{false} Pick date /PopoverTrigger判断口诀只要渲染目标是a、span这类非button元素base 体系就补一个nativeButton{false}radix 体系没有这个 prop用asChild即可。Select 差异items prop、placeholder 与定位Select 是两套 API 差异最大的组件之一共有四个差异点。items propbase 必须在根节点声明base 体系要求根Select携带items数组数据驱动radix 体系只使用内联 JSX。错误base——缺少itemsSelect SelectTriggerSelectValue placeholderSelect a fruit //SelectTrigger /Select正确baseconst items [ { label: Select a fruit, value: null }, { label: Apple, value: apple }, { label: Banana, value: banana }, ] Select items{items} SelectTrigger SelectValue / /SelectTrigger SelectContent SelectGroup {items.map((item) ( SelectItem key{item.value} value{item.value}{item.label}/SelectItem ))} /SelectGroup /SelectContent /Select正确radix——纯内联 JSXSelect SelectTrigger SelectValue placeholderSelect a fruit / /SelectTrigger SelectContent SelectGroup SelectItem valueappleApple/SelectItem SelectItem valuebananaBanana/SelectItem /SelectGroup /SelectContent /Selectplaceholderbase 用value: null项radix 用placeholderpropbase在items数组里放一个{ label: ..., value: null }项SelectValue /不再需要placeholderradixSelectValue placeholderSelect a fruit /。迁移时的典型错误就是把 radix 的placeholder...原样带到 base 版本结果占位文案不显示——base 的占位是数据不是 prop。内容定位alignItemWithTriggervsposition两套体系对浮层定位的 prop 命名不同// base. SelectContent alignItemWithTrigger{false} sidebottom // radix. SelectContent positionpopperbasealignItemWithTrigger{false}表示列表不强制与选中项对齐radixpositionpopper表示用 popper 定位相对触发器否则为item-aligned。Select 多选与对象值base 独有base 体系支持multiple、SelectValue的渲染函数 children以及配合itemToStringValue的对象值radix 体系的Select只支持单选且值只能是字符串。这两组能力在跨体系迁移时没有对等物需要改用Combobox等其他组件替代radix 侧多选通常由 Combobox 承担。base——多选Select items{items} multiple defaultValue{[]} SelectTrigger SelectValue {(value: string[]) value.length 0 ? Select fruits : ${value.length} selected} /SelectValue /SelectTrigger ... /Selectbase——对象值Select defaultValue{plans[0]} itemToStringValue{(plan) plan.name} SelectTrigger SelectValue{(value) value.name}/SelectValue /SelectTrigger ... /Select两个要点itemToStringValue负责把对象值序列化为字符串用于内部状态与无障碍播报SelectValue的 children 作为渲染函数接收当前值可渲染对象上的任意字段。radix 体系两者皆无遇到对象值场景不要硬套Select。ToggleGroupradix 用typebase 用multiple布尔值单选/多选的开关在两套体系中表达方式不同且base 的defaultValue永远是数组radix 单选时是字符串。错误base——误用 radix 的typepropToggleGroup typesingle defaultValuedaily ToggleGroupItem valuedailyDaily/ToggleGroupItem /ToggleGroup正确base// Single (no prop needed), defaultValue is always an array. ToggleGroup defaultValue{[daily]} spacing{2} ToggleGroupItem valuedailyDaily/ToggleGroupItem ToggleGroupItem valueweeklyWeekly/ToggleGroupItem /ToggleGroup // Multi-selection. ToggleGroup multiple ToggleGroupItem valueboldBold/ToggleGroupItem ToggleGroupItem valueitalicItalic/ToggleGroupItem /ToggleGroup正确radix// Single, defaultValue is a string. ToggleGroup typesingle defaultValuedaily spacing{2} ToggleGroupItem valuedailyDaily/ToggleGroupItem ToggleGroupItem valueweeklyWeekly/ToggleGroupItem /ToggleGroup // Multi-selection. ToggleGroup typemultiple ToggleGroupItem valueboldBold/ToggleGroupItem ToggleGroupItem valueitalicItalic/ToggleGroupItem /ToggleGroup对照总结能力baseradix单选默认无需 proptypesingle多选multipletypemultipledefaultValue类型恒为数组[daily]单选为字符串daily受控单选值的差异最能体现“base 一切皆数组”的设计// base — wrap/unwrap arrays. const [value, setValue] React.useState(normal) ToggleGroup value{[value]} onValueChange{(v) setValue(v[0])} // radix — plain string. const [value, setValue] React.useState(normal) ToggleGroup typesingle value{value} onValueChange{setValue}base 侧需要在边界处做“数组包装 / 拆包”value{[value]}进、v[0]出radix 侧直接透传字符串。迁移受控组件时漏掉这层包装是典型故障点。Sliderbase 单滑块可传标量radix 恒为数组Base 对单滑块接受普通数字Radix 一律要求数组因为 Radix Slider 原生就是多滑块模型。错误base——给单滑块传数组Slider defaultValue{[50]} max{100} step{1} /正确baseSlider defaultValue{50} max{100} step{1} /正确radixSlider defaultValue{[50]} max{100} step{1} /范围滑块range slider两边都用数组。受控的onValueChange在 base 下可能需要类型断言// base. const [value, setValue] React.useState([0.3, 0.7]) Slider value{value} onValueChange{(v) setValue(v as number[])} / // radix. const [value, setValue] React.useState([0.3, 0.7]) Slider value{value} onValueChange{setValue} /base 中onValueChange的回调参数类型同时覆盖标量与数组两种形态因此范围滑块场景需要v as number[]断言后写入 state。Accordiontype/collapsibleradixvsmultiplebaseRadix 要求显式typesingle或typemultiple并支持collapsible允许收起已展开项defaultValue是字符串。Base 没有typeprop用multiple布尔值控制多选defaultValue永远是数组。错误base——照搬 radix 的写法Accordion typesingle collapsible defaultValueitem-1 AccordionItem valueitem-1.../AccordionItem /Accordion正确baseAccordion defaultValue{[item-1]} AccordionItem valueitem-1.../AccordionItem /Accordion // Multi-select. Accordion multiple defaultValue{[item-1, item-2]} AccordionItem valueitem-1.../AccordionItem AccordionItem valueitem-2.../AccordionItem /Accordion正确radixAccordion typesingle collapsible defaultValueitem-1 AccordionItem valueitem-1.../AccordionItem /Accordion迁移与双目录维护仓库给出的工程化支撑上述逐组件差异之所以值得沉淀成规则文档是因为仓库中存在大量从 radix 到 base 的存量迁移与并行维护工作。两点工程化事实可以作为佐证并行注册表结构apps/v4/registry/bases 目录下base/与radix/两套组件一一对应如 base/ui/accordion.tsx 与 radix 侧同名文件bases README 要求共享表面的变更同时落到两边仅调整导入路径与原语 API。迁移技能与映射表仓库自带 migrate-radix-to-base 技能并按组件类别拆分了 menus.md、overlays.md、form-controls.md 等映射文档与 shadcn 技能规则目录 互为配套CLI 侧也提供 migrate 命令 供项目执行迁移。对开发者而言日常只需记住规则文档的判定路径先看info输出的base字段确定体系再按对应一侧的写法实现组件跨项目复制粘贴代码时把asChild/render、type/multiple、字符串 / 数组defaultValue、placeholder/value: null这四组差异当成必查清单。速查表场景baseBase UIradixRadix UI元素替换render{Button /}asChild 子元素渲染为非 button 元素追加nativeButton{false}无需额外 propSelect 选项根节点items数组内联SelectItemJSXSelect 占位{ label, value: null }项SelectValue placeholder... /Select 定位alignItemWithTriggerpositionpopperSelect 多选 / 对象值multiple、itemToStringValue、渲染函数 children不支持仅单选字符串值ToggleGroup 多选multipletypemultipleToggleGroup 单选默认typesingleToggleGroupdefaultValue恒为数组单选为字符串Slider 单滑块值标量50数组[50]Accordion 类型无typemultiple布尔typesingle \| multiple支持collapsibleAccordiondefaultValue恒为数组单选为字符串参考依据规则文档 base-vs-radix.md、info 命令实现、getBase 取值逻辑、注册表并行结构说明 以及 base 侧组件源码如 alert-dialog.tsx 的render用法。【免费下载链接】uiA set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.项目地址: https://gitcode.com/GitHub_Trending/ui/ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考