ui-sdk 的部件在服务端渲染初始 HTML,包括 ARIA 状态与键盘 tab 焦点;水合(hydration)在浏览器里接上事件处理器。因此首屏就有正确的语义与可访问性,原生 HTML 行为(如表单提交)甚至在水合前就能工作。
从服务端组件渲染
在支持 React Server Components 的应用(如 Next.js)里,可以直接从服务端页面渲染 ui-sdk 组件——它们的模块已自带 "use client":
import { Toggle } from "@ui-sdk/react/toggle";
function Page() {
return <Toggle defaultPressed>加粗</Toggle>;
}服务端发出一个带 aria-pressed="true" 的按钮,水合后即可切换。
服务端里的包装器
只渲染标记的包装器可以留在服务端。renderElement 让它支持与别的部件相同的 render 属性:
import { renderElement, type PartProps } from "@ui-sdk/react/primitive";
function Card({ render, ...props }: PartProps<"div">) {
return renderElement("div", { ...props, "data-card": "" }, render);
}
<Card render={<section />} className="card">
你的套餐将于 5 月 1 日续订。
</Card>;用 usePrimitive、usePart 或 createParts 构建的自定义部件要放在客户端模块里,服务端组件再渲染这些导出的部件。参见行为与自定义组件。
加上你的客户端边界
如果你的组件持有 React 状态、回调或渲染函数,在它的入口文件顶部加 "use client":
"use client";
import { Toggle } from "@ui-sdk/react/toggle";
import { useState } from "react";
export function MuteButton() {
const [muted, setMuted] = useState(false);
return (
<>
<Toggle pressed={muted} onPressedChange={setMuted}>
静音
</Toggle>
<output>{muted ? "声音已关" : "声音已开"}</output>
</>
);
}从服务端页面渲染 MuteButton。普通回调与渲染函数要留在客户端边界内,它们不能作为属性从服务端组件跨过来。
偏好与存储
配色方案、emoji 肤色这类偏好保存在浏览器里,服务端无法渲染。把 Preferences 放在 <body> 顶部:服务端会渲染一段内联脚本作为它的第一个子节点,在页面加载时(水合前)把每个已保存的值写到 data-* 属性上,于是选择这些属性的 CSS 从首帧起就正确。
"use client";
import { Preferences } from "@ui-sdk/react/preferences";
import { colorScheme, skinTone } from "./preferences";
export function AppPreferences({ children }: { children: React.ReactNode }) {
return <Preferences preferences={[colorScheme, skinTone]}>{children}</Preferences>;
}global: true 的偏好写在 <html> 上,脚本在水合前设置它,所以 <html> 需要 suppressHydrationWarning。若你的 CSP 只允许带 nonce 的内联脚本,给 Preferences 传 nonce。
通过 usePreference 读取偏好的内容,服务端渲染默认值、水合后立即换成已保存值。首屏必须正确的地方,改用属性选择器写样式。保存在 cookie 里的偏好可以在服务端读取请求的 Cookie 头并传给根部,从而连内容一起服务端渲染。详见偏好设置。
让首屏渲染一致
服务端与客户端用相同的初始值。浏览器独有的值放到 effect 或事件处理器里、水合之后再读。
ID:部件为它们的关系生成 ID(如对话框触发器的 aria-controls、字段标签的 for),通常无需你提供。当组件外部需要引用某个部件时才传显式 id。服务端渲染顺序有影响:若字段标签先于带你的 ID 的控件渲染,标签起初用生成的 ID,水合再更新为你的。把带你的 ID 的部件与引用它的部件放在同一个 Suspense 边界里,让它们一起水合。
用类名或 data 属性写样式,不要用生成的 ID。弹出层就地渲染,服务端布局里不需要 portal 根节点。