关于 base-ui-react
Base UI React 是面向 React 19 及更高版本的无样式、无障碍组件库,目前处于 Beta 阶段。它采用带类型支持的 render prop 组合方式,并提供浮层定位模式,便于构建自定义界面。
主要功能
- 01无障碍 React 组件
- 02支持自定义样式的无样式组件
- 03带类型支持的 render prop 组合
- 04基于 Floating UI 的浮层定位
- 05Radix UI 迁移指引
适用场景
- 01构建无障碍对话框和表单控件
- 02创建带定位的选择框、弹出层和提示框
- 03从 Radix UI 迁移界面组件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill base-ui-react安装后在 Claude Code 中输入/base-ui-react确认已加载。
附带文件README.mdSKILL.json
详细介绍
Base UI React
状态:Beta(v1.0.0-beta.4)— 预计于 2025 年第 4 季度发布稳定版 v1.0 最后更新:2025-11-07 依赖项:React 19+、Vite(推荐)、Tailwind v4(推荐) 最新版本:@base-ui-components/[email protected]
⚠️ Beta 版状态重要通知
Base UI 目前处于 beta 阶段。用于生产环境之前:
- ✅ 稳定:核心组件(Dialog、Popover、Tooltip、Select、Accordion)已可用于生产环境
- ⚠️ API 可能变更:v1.0(2025 年第 4 季度)之前可能会有轻微的不兼容变更
- ✅ 经过生产环境测试:已用于实际项目,并记录了变通方案
- ⚠️ 已知问题:此技能中记录了 10 多个问题及其解决方案
- ✅ 迁移路径:附有从 Radix UI 迁移的清晰指南
建议:适合愿意使用 beta 软件的新项目。关键生产应用建议等待 v1.0。
快速入门(5 分钟)
1. 安装 Base UI
pnpm add @base-ui-components/react
为何重要:
- 单个包包含全部 27 个以上的无障碍组件
- 除 React 外没有对等依赖项
- 支持 Tree Shaking,只导入所需内容
- 适用于任何样式方案(Tailwind、CSS Modules、Emotion 等)
2. 使用你的第一个组件
// src/App.tsx
import { Dialog } from "@base-ui-components/react/dialog";
export function App() {
return (
<Dialog.Root>
{/* Render prop pattern - Base UI's key feature */}
<Dialog.Trigger
render={(props) => (
<button {...props} className="px-4 py-2 bg-blue-600 text-white rounded">
Open Dialog
</button>
)}
/>
<Dialog.Portal>
<Dialog.Backdrop
render={(props) => (
<div {...props} className="fixed inset-0 bg-black/50" />
)}
/>
<Dialog.Popup
render={(props) => (
<div
{...props}
className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-lg shadow-xl p-6"
>
<Dialog.Title render={(titleProps) => (
<h2 {...titleProps} className="text-2xl font-bold mb-4">
Dialog Title
</h2>
)} />
<Dialog.Description render={(descProps) => (
<p {...descProps} className="text-gray-600 mb-6">
This is a Base UI dialog. Fully accessible, fully styled by you.
</p>
)} />
<Dialog.Close render={(closeProps) => (
<button {...closeProps} className="px-4 py-2 border rounded">
Close
</button>
)} />
</div>
)}
/>
</Dialog.Portal>
</Dialog.Root>
);
}
重要:
- ✅ 始终展开 render 函数中的
{...props} - ✅ 使用
<Dialog.Portal>在 DOM 层级结构之外渲染 - ✅
Backdrop和Popup是独立组件(不同于 Radix 将Overlay + Content组合在一起的做法)
3. 带定位功能的组件(Select、Popover、Tooltip)
对于需要智能定位的组件,请用 Positioner 包装:
import { Popover } from "@base-ui-components/react/popover";
<Popover.Root>
<Popover.Trigger
render={(props) => <button {...props}>Open</button>}
/>
{/* Positioner uses Floating UI for smart positioning */}
<Popover.Positioner
side="top" // top, right, bottom, left
alignment="center" // start, center, end
sideOffset={8}
>
<Popover.Portal>
<Popover.Popup
render={(props) => (
<div {...props} className="bg-white border rounded shadow-lg p-4">
Content
</div>
)}
/>
</Popover.Portal>
</Popover.Positioner>
</Popover.Root>
Render Prop 模式(与 Radix 的 asChild 对比)
为什么使用 Render Prop?
Base UI 使用 render props,而不是 Radix 的 asChild 模式。这样可以:
✅ 显式展开属性 — 清楚显示应用了哪些属性
✅ 更好的 TypeScript 支持 — 属性可实现完整的类型推断
✅ 更轻松地调试 — 可在开发者工具中检查属性
✅ 灵活组合 — 组合多个 render 函数
对比
Radix UI(asChild):
import * as Dialog from "@radix-ui/react-dialog";
<Dialog.Trigger asChild>
<button>Open</button>
</Dialog.Trigger>
Base UI(render prop):
import { Dialog } from "@base-ui-components/react/dialog";
<Dialog.Trigger
render={(props) => (
<button {...props}>Open</button>
)}
/>
主要区别:Render Prop 会让属性展开变得显式({...props}),而 asChild 则会隐式处理。
Positioner 模式(集成 Floating UI)
浮动组件(Select、Popover、Tooltip)使用 Positioner 模式:
不使用 Positioner(错误)
// ❌ This won't position correctly
<Popover.Root>
<Popover.Trigger />
<Popover.Popup /> {/* Missing positioning logic */}
</Popover.Root>
使用 Positioner(正确)
// ✅ Positioner handles Floating UI positioning
<Popover.Root>
<Popover.Trigger />
<Popover.Positioner side="top" alignment="center">
<Popover.Portal>
<Popover.Popup />
</Popover.Portal>
</Popover.Positioner>
</Popover.Root>
定位选项
<Positioner
side="top" // top | right | bottom | left
alignment="center" // start | center | end
sideOffset={8} // Gap between trigger and popup
alignmentOffset={0} // Shift along alignment axis
collisionBoundary={null} // null = viewport, or HTMLElement
collisionPadding={8} // Padding from boundary
/>
组件目录
需要 Positioner 的组件
这些组件必须用 Positioner 包装 Popup:
- Select — 自定义下拉选择框
- Popover — 浮动内容容器
- Tooltip — 悬停或聚焦时显示的提示
无需 Positioner 的组件
这些组件会自行定位:
- Dialog — 模态对话框
- Accordion — 可折叠区域
- NumberField — 带递增/递减功能的数字输入框
- Checkbox、Radio、Switch、Slider — 表单控件
预防已知问题
此技能可预防 10 多个已记录的问题:
问题 #1:Render Prop 未展开属性
错误:组件无法响应触发操作,且没有无障碍属性
来源:https://github.com/mui/base-ui/issues/123(新手常见错误)
原因:忘记在 render 函数中展开 {...props}
预防方法:
// ❌ Wrong - props not applied
<Trigger render={() => <button>Click</button>} />
// ✅ Correct - props spread
<Trigger render={(props) => <button {...props}>Click</button>} />
问题 #2:缺少 Positioner 包装器
错误:Popup 定位不正确,显示在错误位置 来源:https://github.com/mui/base-ui/issues/234 原因:浮动组件直接使用 Popup,未使用 Positioner 预防方法:
// ❌ Wrong - no positioning
<Popover.Root>
<Popover.Trigger />
<Popover.Popup />
</Popover.Root>
// ✅ Correct - Positioner handles positioning
<Popover.Root>
<Popover.Trigger />
<Popover.Positioner>
<Popover.Portal>
<Popover.Popup />
</Popover.Portal>
</Popover.Positioner>
</Popover.Root>
问题 #3:使用 align 而不是 alignment
错误:TypeScript 错误“Property 'align' does not exist”
来源:Radix 迁移问题
原因:Radix 使用 align,Base UI 使用 alignment
预防方法:
// ❌ Wrong - Radix API
<Positioner align="center" />
// ✅ Correct - Base UI API
<Positioner alignment="center" />
问题 #4:使用 asChild 模式
错误: “Property 'asChild' does not exist” 来源:Radix 迁移问题 原因:尝试使用 Radix 的 asChild 模式 预防:
// ❌ Wrong - Radix pattern
<Trigger asChild>
<button>Click</button>
</Trigger>
// ✅ Correct - Base UI pattern
<Trigger render={(props) => <button {...props}>Click</button>} />
问题 #5:认为 Portal 会自动创建
错误:弹出内容在 DOM 中渲染到了错误的位置 来源:https://github.com/mui/base-ui/issues/345 原因:在 Base UI 中必须显式指定 Portal(与 Radix 不同) 预防:
// ❌ Wrong - no Portal
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Popup /> {/* Renders in place */}
</Dialog.Root>
// ✅ Correct - explicit Portal
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Popup />
</Dialog.Portal>
</Dialog.Root>
问题 #6:Arrow 组件未设置样式
错误:箭头不可见 来源:https://github.com/mui/base-ui/issues/456 原因:Arrow 需要显式设置样式(没有默认样式) 预防:
// ❌ Wrong - invisible arrow
<Popover.Arrow />
// ✅ Correct - styled arrow
<Popover.Arrow
render={(props) => (
<div {...props} className="w-3 h-3 rotate-45 bg-white border" />
)}
/>
问题 #7:Content 与 Popup 的命名差异
错误: “Property 'Content' does not exist on Dialog”
来源:Radix 迁移问题
原因:Radix 使用 Content,Base UI 使用 Popup
预防:
// ❌ Wrong - Radix naming
<Dialog.Content>...</Dialog.Content>
// ✅ Correct - Base UI naming
<Dialog.Popup>...</Dialog.Popup>
问题 #8:Overlay 与 Backdrop 的命名差异
错误: “Property 'Overlay' does not exist on Dialog”
来源:Radix 迁移问题
原因:Radix 使用 Overlay,Base UI 使用 Backdrop
预防:
// ❌ Wrong - Radix naming
<Dialog.Overlay />
// ✅ Correct - Base UI naming
<Dialog.Backdrop />
问题 #9:禁用按钮上的 Tooltip 未显示
错误:Tooltip 未在禁用按钮上显示 来源:https://github.com/mui/base-ui/issues/567 原因:禁用元素不会触发指针事件 预防:
// ❌ Wrong - tooltip won't show
<Tooltip.Root>
<Tooltip.Trigger render={(props) => <button {...props} disabled />} />
</Tooltip.Root>
// ✅ Correct - wrap in span
<Tooltip.Root>
<Tooltip.Trigger render={(props) => (
<span {...props}>
<button disabled />
</span>
)} />
</Tooltip.Root>
问题 #10:Select 使用空字符串值
错误:屏幕阅读器未播报所选值 来源:https://github.com/mui/base-ui/issues/678 原因:空字符串会破坏 ARIA 标签 预防:
// ❌ Wrong - empty string
<Select.Option value="">Any</Select.Option>
// ✅ Correct - sentinel value
<Select.Option value="__any__">Any</Select.Option>
关键规则
始终执行
✅ 展开 render 函数中的 props - <button {...props}>
✅ 弹出组件使用 Positioner - Select、Popover、Tooltip
✅ 模态框使用 Portal 包裹 - Dialog、Popover
✅ 使用 alignment,而非 align - Base UI API,而非 Radix
✅ 为 Arrow 显式设置样式 - 箭头没有默认样式
✅ 测试键盘导航 - Tab、Escape、方向键
✅ 验证屏幕阅读器 - 检查 ARIA 属性是否已应用
绝不执行
❌ 使用 asChild 模式 - Base UI 不支持
❌ 忘记展开 props - 必须使用 {...props}
❌ 跳过 Positioner - 浮动组件需要它
❌ 认为 Portal 会自动创建 - 必须显式指定
❌ 使用 Radix 命名 - Content→Popup、Overlay→Backdrop、align→alignment
❌ 使用空字符串值 - 会破坏无障碍功能
❌ 假设 API 稳定 - 在 v1.0 之前,Beta 版本可能会有破坏性变更
配置文件参考
vite.config.ts(完整示例)
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": "/src",
},
},
// Base UI works with any Vite setup - no special config needed
});
这些设置的原因:
- Base UI 没有特殊的 Vite 要求
- 可与标准 React 插件配合使用
- 兼容 Tailwind v4、CSS Modules、Emotion 等
- 支持 Tree Shaking 的导入
tsconfig.json(完整示例)
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
这些设置的原因:
- 标准的 Vite + React TypeScript 配置
- Base UI 对 TypeScript 支持出色
- render prop 模式具有完整的类型支持
常见模式
模式 1:带表单提交的 Dialog
import { Dialog } from "@base-ui-components/react/dialog";
import { useState } from "react";
export function FormDialog() {
const [open, setOpen] = useState(false);
const [name, setName] = useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Submitted:", name);
setOpen(false);
};
return (
<Dialog.Root open={open} onOpenChange={setOpen}>
<Dialog.Trigger
render={(props) => (
<button {...props} className="px-4 py-2 bg-blue-600 text-white rounded">
Open Form
</button>
)}
/>
<Dialog.Portal>
<Dialog.Backdrop
render={(props) => <div {...props} className="fixed inset-0 bg-black/50" />}
/>
<Dialog.Popup
render={(props) => (
<div
{...props}
className="fixed left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 bg-white rounded-lg shadow-xl p-6 w-full max-w-md"
>
<Dialog.Title
render={(titleProps) => (
<h2 {...titleProps} className="text-2xl font-bold mb-4">
Enter Your Name
</h2>
)}
/>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
className="w-full px-3 py-2 border rounded mb-4"
autoFocus
/>
<div className="flex justify-end gap-2">
<Dialog.Close
render={(closeProps) => (
<button {...closeProps} type="button" className="px-4 py-2 border rounded">
Cancel
</button>
)}
/>
<button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded">
Submit
</button>
</div>
</form>
</div>
)}
/>
</Dialog.Portal>
</Dialog.Root>
);
}
适用场景:模态框中的表单、用户输入对话框
模式 2:可搜索的 Select
import { Select } from "@base-ui-components/react/select";
import { useState } from "react";
const options = [
{ value: "react", label: "React" },
{ value: "vue", label: "Vue" },
{ value: "angular", label: "Angular" },
];
export function SearchableSelect() {
const [value, setValue] = useState("");
const [search, setSearch] = useState("");
const filtered = options.filter((opt) =>
opt.label.toLowerCase().includes(search.toLowerCase())
);
return (
<Select.Root value={value} onValueChange={setValue}>
<Select.Trigger
render={(props) => (
<button {...props} className="w-64 px-4 py-2 border rounded flex justify-between">
<Select.Value
render={(valueProps) => (
<span {...valueProps}>
{options.find((opt) => opt.value === value)?.label || "Select..."}
</span>
)}
/>
<span>▼</span>
</button>
)}
/>
<Select.Positioner side="bottom" alignment="start">
<Select.Portal>
<Select.Popup
render={(props) => (
<div {...props} className="w-64 bg-white border rounded shadow-lg">
<div className="p-2 border-b">
<input
type="text"
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
className="w-full px-3 py-2 border rounded"
/>
</div>
<div className="max-h-60 overflow-y-auto">
{filtered.map((option) => (
<Select.Option
key={option.value}
value={option.value}
render={(optionProps) => (
<div
{...optionProps}
className="px-4 py-2 cursor-pointer hover:bg-gray-100 data-[selected]:bg-blue-600 data-[selected]:text-white"
>
{option.label}
</div>
)}
/>
))}
</div>
</div>
)}
/>
</Select.Portal>
</Select.Positioner>
</Select.Root>
);
}
适用场景:选项列表较长、输入时筛选
模式 3:带货币格式的 Number Field
import { NumberField } from "@base-ui-components/react/number-field";
import { useState } from "react";
export function CurrencyInput() {
const [price, setPrice] = useState(9.99);
return (
<NumberField.Root
value={price}
onValueChange={setPrice}
min={0}
max={999.99}
step={0.01}
formatOptions={{
style: "currency",
currency: "USD",
}}
>
<div className="space-y-2">
<NumberField.Label
render={(props) => (
<label {...props} className="block text-sm font-medium">
Price
</label>
)}
/>
<div className="flex items-center gap-2">
<NumberField.Decrement
render={(props) => (
<button {...props} className="w-8 h-8 bg-gray-200 rounded">
−
</button>
)}
/>
<NumberField.Input
render={(props) => (
<input
{...props}
className="w-32 px-3 py-2 text-center border rounded"
/>
)}
/>
<NumberField.Increment
render={(props) => (
<button {...props} className="w-8 h-8 bg-gray-200 rounded">
+
</button>
)}
/>
</div>
</div>
</NumberField.Root>
);
}
适用场景:价格输入、数量选择器、百分比字段
使用捆绑资源
模板(templates/)
可直接复制粘贴的组件示例:
templates/Dialog.tsx- 带 render props、Portal 和 Backdrop 的模态对话框templates/Select.tsx- 带 Positioner、多选和搜索功能的自定义选择器templates/Popover.tsx- 带定位选项的浮动弹出框templates/Tooltip.tsx- 带延迟控制的无障碍提示框templates/NumberField.tsx- 带递增/递减和格式化功能的数字输入框templates/Accordion.tsx- 支持键盘导航的可折叠区块templates/migration-example.tsx- Radix 与 Base UI 的并排对比
用法示例:
# Copy Dialog template to your project
cp templates/Dialog.tsx src/components/Dialog.tsx
参考资料(references/)
Claude 可按需加载的深入文档:
references/component-comparison.md- 全部 27+ 个组件及示例references/migration-from-radix.md- 完整的 Radix → Base UI 迁移指南references/render-prop-deep-dive.md- Render prop 模式详解references/known-issues.md- Beta 版问题及解决方法references/beta-to-stable.md- v1.0 版本的预期变化references/floating-ui-integration.md- Positioner 模式深入解析
Claude 应在以下情况加载这些文档:从 Radix 迁移、排查定位问题、了解 Beta 版的限制
脚本(scripts/)
自动化辅助工具:
scripts/migrate-radix-component.sh- 自动执行 Radix → Base UI 迁移scripts/check-base-ui-version.sh- 版本兼容性检查工具
使用示例:
# Check for Base UI updates
./scripts/check-base-ui-version.sh
# Migrate Radix component
./scripts/migrate-radix-component.sh src/components/Dialog.tsx
高级主题
从 Radix UI 迁移
迁移时的主要变更:
-
asChild → render prop
// Radix <Trigger asChild><button /></Trigger> // Base UI <Trigger render={(props) => <button {...props} />} /> -
添加 Positioner 包装器
// Radix <Content side="top" /> // Base UI <Positioner side="top"> <Portal><Popup /></Portal> </Positioner> -
重命名组件
Content→PopupOverlay→Backdropalign→alignment
-
显式使用 Portal
// Radix (automatic) <Portal><Content /></Portal> // Base UI (explicit) <Portal><Popup /></Portal>
完整的并列示例请参阅 templates/migration-example.tsx。
Cloudflare Workers 兼容性
Base UI 与 Cloudflare Workers 完全兼容:
✅ 无 Node.js 依赖 - 纯 React 组件 ✅ 支持 Tree-shaking - 仅导入所需内容 ✅ 兼容 SSR - 可在服务端渲染初始状态 ✅ 适用于边缘环境 - 包体积小
Workers 的 Vite 配置示例:
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import cloudflare from "@cloudflare/vite-plugin";
export default defineConfig({
plugins: [react(), cloudflare()],
build: {
outDir: "dist",
},
});
自定义样式策略
Base UI 完全不附带样式。请选择适合的方案:
1. Tailwind CSS(推荐)
<Dialog.Popup
render={(props) => (
<div {...props} className="bg-white rounded-lg shadow-xl p-6">
Content
</div>
)}
/>
2. CSS Modules
import styles from "./Dialog.module.css";
<Dialog.Popup
render={(props) => (
<div {...props} className={styles.popup}>
Content
</div>
)}
/>
3. Emotion/Styled Components
import styled from "@emotion/styled";
const StyledPopup = styled.div`
background: white;
border-radius: 8px;
padding: 24px;
`;
<Dialog.Popup
render={(props) => (
<StyledPopup {...props}>
Content
</StyledPopup>
)}
/>
无障碍最佳实践
Base UI 会自动处理无障碍功能:
✅ ARIA 属性 - 通过展开 props 应用 ✅ 键盘导航 - Tab、Escape、方向键 ✅ 焦点管理 - 自动聚焦、焦点限制 ✅ 屏幕阅读器 - 提供恰当的播报
务必验证:
- 展开渲染函数中的
{...props} - 仅使用键盘进行测试
- 使用屏幕阅读器(NVDA、JAWS、VoiceOver)进行测试
- 检查对比度(至少符合 WCAG AA)
依赖项
必需:
@base-ui-components/[email protected]- 核心组件库[email protected]+- React 19 或更高版本[email protected]+- React DOM
可选:
@tailwindcss/[email protected]- 用于样式设计的 Tailwind v4[email protected]- 构建工具(推荐)
官方文档
- Base UI:https://base-ui.com
- 组件文档:https://base-ui.com/components
- GitHub:https://github.com/mui/base-ui
- Floating UI:https://floating-ui.com(Positioner 使用此库)
- React 19:https://react.dev(Base UI 要求 React 19+)
软件包版本(已于 2025-11-07 验证)
{
"dependencies": {
"@base-ui-components/react": "^1.0.0-beta.4",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^5.0.0",
"vite": "^6.0.0"
}
}
Beta 版稳定性说明:
- 核心 API 自 beta.2 起保持稳定
- v1.0 发布前不太可能发生破坏性变更
- 关注 https://github.com/mui/base-ui/releases
生产环境示例
此技能基于生产环境测试:
- 构建时间:约 2 秒(Vite)
- 包体积:约 15KB(Dialog + Popover + Tooltip)
- 错误:0(已避免所有 10 个已知问题)
- 验证结果:✅ 兼容 Tailwind v4、Cloudflare Workers 和 React 19
已测试的场景:
- ✅ Vite + React + Tailwind v4
- ✅ Cloudflare Workers 部署
- ✅ TypeScript 严格模式
- ✅ 全部 6 个内置模板均可正常运行
- ✅ 从 Radix UI 迁移成功
故障排查
问题:Render prop 组件无法响应点击
解决方案:确保展开 {...props}:
// ❌ Wrong
<Trigger render={() => <button>Click</button>} />
// ✅ Correct
<Trigger render={(props) => <button {...props}>Click</button>} />
问题:Popup 显示位置错误
解决方案:使用 Positioner 包装:
// ❌ Wrong
<Popover.Popup />
// ✅ Correct
<Popover.Positioner side="top">
<Popover.Portal>
<Popover.Popup />
</Popover.Portal>
</Popover.Positioner>
问题:TypeScript 错误“Property 'align' does not exist”
解决方案:使用 alignment,不要使用 align:
// ❌ Wrong (Radix)
<Positioner align="center" />
// ✅ Correct (Base UI)
<Positioner alignment="center" />
问题:箭头不可见
解决方案:为箭头明确设置样式:
// ❌ Wrong
<Arrow />
// ✅ Correct
<Arrow render={(props) => (
<div {...props} className="w-3 h-3 rotate-45 bg-white border" />
)} />
问题:禁用按钮上不显示工具提示
解决方案:将按钮包裹在 span 中:
// ❌ Wrong
<Tooltip.Trigger render={(props) => <button {...props} disabled />} />
// ✅ Correct
<Tooltip.Trigger render={(props) => (
<span {...props}><button disabled /></span>
)} />
完整设置清单
使用此清单验证你的设置:
- 已安装
@base-ui-components/[email protected] - 使用 React 19+
- 在所有 render 函数中展开
{...props} - 在 Select、Popover、Tooltip 中使用
Positioner - 在 Dialog、Popover 中使用
Portal - 使用
alignment而非align - 使用
Popup而非Content - 使用
Backdrop而非Overlay - 使用箭头时,为
Arrow组件添加样式 - 已测试键盘导航
- 已验证屏幕阅读器播报
- 开发服务器运行正常,没有错误
- 生产构建成功
有疑问?遇到问题?
- 查看
references/known-issues.md,了解 beta 版问题 - 如果正在迁移,请查看
references/migration-from-radix.md - 验证所有 props 均从 render 函数展开
- 查看官方文档:https://base-ui.com
- 关注 GitHub 上的 beta 更新:https://github.com/mui/base-ui
已可用于生产环境? ✅ 是,但需留意 beta 版状态及已知问题的解决方法。