Loading...
Loading...
Compare original and translation side by side
pnpm add @base-ui-components/reactpnpm add @base-ui-components/react// 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>
);
}{...props}<Dialog.Portal>BackdropPopupOverlay + Content// src/App.tsx
import { Dialog } from "@base-ui-components/react/dialog";
export function App() {
return (
<Dialog.Root>
{/* Render prop模式——Base UI的核心特性 */}
<Dialog.Trigger
render={(props) => (
<button {...props} className="px-4 py-2 bg-blue-600 text-white rounded">
打开对话框
</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">
对话框标题
</h2>
)} />
<Dialog.Description render={(descProps) => (
<p {...descProps} className="text-gray-600 mb-6">
这是一个Base UI对话框,完全支持可访问性,样式完全由你定制。
</p>
)} />
<Dialog.Close render={(closeProps) => (
<button {...closeProps} className="px-4 py-2 border rounded">
关闭
</button>
)} />
</div>
)}
/>
</Dialog.Portal>
</Dialog.Root>
);
}{...props}<Dialog.Portal>BackdropPopupOverlay + ContentPositionerimport { 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>Positionerimport { Popover } from "@base-ui-components/react/popover";
<Popover.Root>
<Popover.Trigger
render={(props) => <button {...props}>打开</button>}
/>
{/* Positioner使用Floating UI实现智能定位 */}
<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">
内容
</div>
)}
/>
</Popover.Portal>
</Popover.Positioner>
</Popover.Root>import * as Dialog from "@radix-ui/react-dialog";
<Dialog.Trigger asChild>
<button>Open</button>
</Dialog.Trigger>import { Dialog } from "@base-ui-components/react/dialog";
<Dialog.Trigger
render={(props) => (
<button {...props}>Open</button>
)}
/>{...props}import * as Dialog from "@radix-ui/react-dialog";
<Dialog.Trigger asChild>
<button>打开</button>
</Dialog.Trigger>import { Dialog } from "@base-ui-components/react/dialog";
<Dialog.Trigger
render={(props) => (
<button {...props}>打开</button>
)}
/>{...props}// ❌ This won't position correctly
<Popover.Root>
<Popover.Trigger />
<Popover.Popup /> {/* Missing positioning logic */}
</Popover.Root>// ❌ 定位将失效
<Popover.Root>
<Popover.Trigger />
<Popover.Popup /> {/* 缺少定位逻辑 */}
</Popover.Root>// ✅ 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处理Floating UI定位
<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
side="top" // 可选值:top | right | bottom | left
alignment="center" // 可选值:start | center | end
sideOffset={8} // 触发元素与弹窗之间的间距
alignmentOffset={0} // 沿对齐轴的偏移量
collisionBoundary={null} // null表示视口,也可传入HTMLElement
collisionPadding={8} // 与边界的内边距
/>PopupPositionerPositionerPopup{...props}// ❌ Wrong - props not applied
<Trigger render={() => <button>Click</button>} />
// ✅ Correct - props spread
<Trigger render={(props) => <button {...props}>Click</button>} />{...props}// ❌ 错误写法——未应用属性
<Trigger render={() => <button>点击</button>} />
// ✅ 正确写法——展开属性
<Trigger render={(props) => <button {...props}>点击</button>} />// ❌ 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>// ❌ 错误写法——无定位逻辑
<Popover.Root>
<Popover.Trigger />
<Popover.Popup />
</Popover.Root>
// ✅ 正确写法——Positioner处理定位
<Popover.Root>
<Popover.Trigger />
<Popover.Positioner>
<Popover.Portal>
<Popover.Popup />
</Popover.Portal>
</Popover.Positioner>
</Popover.Root>alignalignment// ❌ Wrong - Radix API
<Positioner align="center" />
// ✅ Correct - Base UI API
<Positioner alignment="center" />alignalignment// ❌ 错误写法——Radix API
<Positioner align="center" />
// ✅ 正确写法——Base UI API
<Positioner alignment="center" />// ❌ Wrong - Radix pattern
<Trigger asChild>
<button>Click</button>
</Trigger>
// ✅ Correct - Base UI pattern
<Trigger render={(props) => <button {...props}>Click</button>} />// ❌ 错误写法——Radix模式
<Trigger asChild>
<button>点击</button>
</Trigger>
// ✅ 正确写法——Base UI模式
<Trigger render={(props) => <button {...props}>点击</button>} />// ❌ 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>// ❌ 错误写法——无Portal
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Popup /> {/* 渲染在当前位置 */}
</Dialog.Root>
// ✅ 正确写法——显式使用Portal
<Dialog.Root>
<Dialog.Trigger />
<Dialog.Portal>
<Dialog.Popup />
</Dialog.Portal>
</Dialog.Root>// ❌ Wrong - invisible arrow
<Popover.Arrow />
// ✅ Correct - styled arrow
<Popover.Arrow
render={(props) => (
<div {...props} className="w-3 h-3 rotate-45 bg-white border" />
)}
/>// ❌ 错误写法——箭头不可见
<Popover.Arrow />
// ✅ 正确写法——设置样式
<Popover.Arrow
render={(props) => (
<div {...props} className="w-3 h-3 rotate-45 bg-white border" />
)}
/>ContentPopup// ❌ Wrong - Radix naming
<Dialog.Content>...</Dialog.Content>
// ✅ Correct - Base UI naming
<Dialog.Popup>...</Dialog.Popup>ContentPopup// ❌ 错误写法——Radix命名
<Dialog.Content>...</Dialog.Content>
// ✅ 正确写法——Base UI命名
<Dialog.Popup>...</Dialog.Popup>OverlayBackdrop// ❌ Wrong - Radix naming
<Dialog.Overlay />
// ✅ Correct - Base UI naming
<Dialog.Backdrop />OverlayBackdrop// ❌ 错误写法——Radix命名
<Dialog.Overlay />
// ✅ 正确写法——Base UI命名
<Dialog.Backdrop />// ❌ 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>// ❌ 错误写法——Tooltip不会显示
<Tooltip.Root>
<Tooltip.Trigger render={(props) => <button {...props} disabled />} />
</Tooltip.Root>
// ✅ 正确写法——用span包裹
<Tooltip.Root>
<Tooltip.Trigger render={(props) => (
<span {...props}>
<button disabled />
</span>
)} />
</Tooltip.Root>// ❌ Wrong - empty string
<Select.Option value="">Any</Select.Option>
// ✅ Correct - sentinel value
<Select.Option value="__any__">Any</Select.Option>// ❌ 错误写法——空字符串
<Select.Option value="">任意</Select.Option>
// ✅ 正确写法——使用哨兵值
<Select.Option value="__any__">任意</Select.Option><button {...props}><button {...props}>{...props}{...props}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
});import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@": "/src",
},
},
// Base UI兼容任何Vite配置——无需特殊设置
});{
"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" }]
}{
"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" }]
}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>
);
}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("已提交:", 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">
打开表单
</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">
输入你的姓名
</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">
取消
</button>
)}
/>
<button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded">
提交
</button>
</div>
</form>
</div>
)}
/>
</Dialog.Portal>
</Dialog.Root>
);
}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>
);
}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 || "请选择..."}
</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="搜索..."
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>
);
}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>
);
}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">
价格
</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/Dialog.tsxtemplates/Select.tsxtemplates/Popover.tsxtemplates/Tooltip.tsxtemplates/NumberField.tsxtemplates/Accordion.tsxtemplates/migration-example.tsxundefinedtemplates/Dialog.tsxtemplates/Select.tsxtemplates/Popover.tsxtemplates/Tooltip.tsxtemplates/NumberField.tsxtemplates/Accordion.tsxtemplates/migration-example.tsxundefinedundefinedundefinedreferences/component-comparison.mdreferences/migration-from-radix.mdreferences/render-prop-deep-dive.mdreferences/known-issues.mdreferences/beta-to-stable.mdreferences/floating-ui-integration.mdreferences/component-comparison.mdreferences/migration-from-radix.mdreferences/render-prop-deep-dive.mdreferences/known-issues.mdreferences/beta-to-stable.mdreferences/floating-ui-integration.mdscripts/migrate-radix-component.shscripts/check-base-ui-version.shundefinedscripts/migrate-radix-component.shscripts/check-base-ui-version.shundefined
---
---// Radix
<Trigger asChild><button /></Trigger>
// Base UI
<Trigger render={(props) => <button {...props} />} />// Radix
<Content side="top" />
// Base UI
<Positioner side="top">
<Portal><Popup /></Portal>
</Positioner>ContentPopupOverlayBackdropalignalignment// Radix (automatic)
<Portal><Content /></Portal>
// Base UI (explicit)
<Portal><Popup /></Portal>templates/migration-example.tsx// Radix
<Trigger asChild><button /></Trigger>
// Base UI
<Trigger render={(props) => <button {...props} />} />// Radix
<Content side="top" />
// Base UI
<Positioner side="top">
<Portal><Popup /></Portal>
</Positioner>ContentPopupOverlayBackdropalignalignment// Radix(自动)
<Portal><Content /></Portal>
// Base UI(显式)
<Portal><Popup /></Portal>templates/migration-example.tsx// 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",
},
});// 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",
},
});<Dialog.Popup
render={(props) => (
<div {...props} className="bg-white rounded-lg shadow-xl p-6">
Content
</div>
)}
/>import styles from "./Dialog.module.css";
<Dialog.Popup
render={(props) => (
<div {...props} className={styles.popup}>
Content
</div>
)}
/>import styled from "@emotion/styled";
const StyledPopup = styled.div`
background: white;
border-radius: 8px;
padding: 24px;
`;
<Dialog.Popup
render={(props) => (
<StyledPopup {...props}>
Content
</StyledPopup>
)}
/><Dialog.Popup
render={(props) => (
<div {...props} className="bg-white rounded-lg shadow-xl p-6">
内容
</div>
)}
/>import styles from "./Dialog.module.css";
<Dialog.Popup
render={(props) => (
<div {...props} className={styles.popup}>
内容
</div>
)}
/>import styled from "@emotion/styled";
const StyledPopup = styled.div`
background: white;
border-radius: 8px;
padding: 24px;
`;
<Dialog.Popup
render={(props) => (
<StyledPopup {...props}>
内容
</StyledPopup>
)}
/>{...props}{...props}@base-ui-components/react@1.0.0-beta.4react@19.2.0+react-dom@19.2.0+@tailwindcss/vite@4.1.14vite@6.0.0@base-ui-components/react@1.0.0-beta.4react@19.2.0+react-dom@19.2.0+@tailwindcss/vite@4.1.14vite@6.0.0{
"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"
}
}{
"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"
}
}{...props}// ❌ Wrong
<Trigger render={() => <button>Click</button>} />
// ✅ Correct
<Trigger render={(props) => <button {...props}>Click</button>} />{...props}// ❌ 错误写法
<Trigger render={() => <button>点击</button>} />
// ✅ 正确写法
<Trigger render={(props) => <button {...props}>点击</button>} />// ❌ Wrong
<Popover.Popup />
// ✅ Correct
<Popover.Positioner side="top">
<Popover.Portal>
<Popover.Popup />
</Popover.Portal>
</Popover.Positioner>// ❌ 错误写法
<Popover.Popup />
// ✅ 正确写法
<Popover.Positioner side="top">
<Popover.Portal>
<Popover.Popup />
</Popover.Portal>
</Popover.Positioner>alignmentalign// ❌ Wrong (Radix)
<Positioner align="center" />
// ✅ Correct (Base UI)
<Positioner alignment="center" />alignmentalign// ❌ 错误写法(Radix)
<Positioner align="center" />
// ✅ 正确写法(Base UI)
<Positioner alignment="center" />// ❌ Wrong
<Arrow />
// ✅ Correct
<Arrow render={(props) => (
<div {...props} className="w-3 h-3 rotate-45 bg-white border" />
)} />// ❌ 错误写法
<Arrow />
// ✅ 正确写法
<Arrow render={(props) => (
<div {...props} className="w-3 h-3 rotate-45 bg-white border" />
)} />// ❌ Wrong
<Tooltip.Trigger render={(props) => <button {...props} disabled />} />
// ✅ Correct
<Tooltip.Trigger render={(props) => (
<span {...props}><button disabled /></span>
)} />// ❌ 错误写法
<Tooltip.Trigger render={(props) => <button {...props} disabled />} />
// ✅ 正确写法
<Tooltip.Trigger render={(props) => (
<span {...props}><button disabled /></span>
)} />@base-ui-components/react@1.0.0-beta.4{...props}PositionerPortalalignmentalignPopupContentBackdropOverlayArrowreferences/known-issues.mdreferences/migration-from-radix.md@base-ui-components/react@1.0.0-beta.4{...props}alignmentalignPopupContentBackdropOverlayreferences/known-issues.mdreferences/migration-from-radix.md{...props}