SSR 使用
Leaf UI 支持服务端渲染。将编译好的样式导入框架入口,再通过 ConfigProvider 提供初始主题即可。
静态样式与动态主题
组件布局、状态和动画已经编译到 @sudden3/leaf-ui/styles.css。ConfigProvider 在渲染时输出当前区域的主题变量,大小尺寸使用 CSS 计算,浏览器直接应用它们。
因此 SSR 和动态主题可以同时保留:服务端输出首屏 HTML 与初始主题,客户端使用同一份配置接管后,再通过 React 状态切换主题。无需 CSS-in-JS 注册器、服务端提取样式、Sass 配置或关闭 SSR。
Next.js App Router
在根布局导入样式
app/layout.tsx
import type { ReactNode } from 'react';
import { cookies } from 'next/headers';
import type { LeafTheme } from '@sudden3/leaf-ui';
import '@sudden3/leaf-ui/styles.css';
import './globals.css';
import { Providers } from './providers';
export default async function RootLayout({ children }: { children: ReactNode }) {
const store = await cookies();
const initialTheme: LeafTheme = {
primaryColor: '#7654c6',
borderRadius: 8,
controlHeight: 34,
appearance: store.get('leaf-appearance')?.value === 'dark' ? 'dark' : 'light',
};
return (
<html lang="zh-CN">
<body>
<Providers initialTheme={initialTheme}>{children}</Providers>
</body>
</html>
);
}
该示例使用当前 Next.js 的异步 cookies API。Cookie 为可选的外观偏好;也可以在服务端读取账户设置,或始终提供固定主题。
创建可以切换主题的区域
app/providers.tsx
'use client';
import { useState, type ReactNode } from 'react';
import { Button, ConfigProvider, type LeafTheme } from '@sudden3/leaf-ui';
export function Providers({
initialTheme,
children,
}: {
initialTheme: LeafTheme;
children: ReactNode;
}) {
const [theme, setTheme] = useState(initialTheme);
function toggleAppearance() {
const appearance = theme.appearance === 'dark' ? 'light' : 'dark';
setTheme((current) => ({ ...current, appearance }));
document.cookie =
'leaf-appearance=' + appearance + '; Path=/; Max-Age=31536000; SameSite=Lax';
}
return (
<ConfigProvider locale="zh-CN" theme={theme}>
<Button onClick={toggleAppearance}>切换外观</Button>
{children}
</ConfigProvider>
);
}
'use client' 为交互组件声明客户端边界,仍会参与 Next.js 的服务端预渲染。包的构建入口也保留了该声明。在 Server Component 中可以导入并渲染 Leaf UI 组件;事件回调、hooks 和交互状态应放在 Client Component 中,传过边界的配置保持可序列化。
将服务端内容作为 children 传给 Providers 可以保留这些内容的 Server Component 边界,不必把整个应用写成客户端组件。
使用表单组件
app/project-form.tsx
'use client';
import { Button, Form, FormField, Input } from '@sudden3/leaf-ui';
export function ProjectForm() {
return (
<Form onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
console.log(data.get('name'));
}}>
<FormField label="项目名称" required>
<Input name="name" />
</FormField>
<Button type="submit">保存</Button>
</Form>
);
}
在页面里导入 ProjectForm 后即可使用,它会继承外层主题。无需 dynamic(..., { ssr: false })。
Next.js Pages Router
在 pages/_app.tsx 导入样式并包裹组件:
pages/_app.tsx
import type { AppProps } from 'next/app';
import { ConfigProvider } from '@sudden3/leaf-ui';
import '@sudden3/leaf-ui/styles.css';
export default function App({ Component, pageProps }: AppProps) {
return (
<ConfigProvider theme={pageProps.initialTheme ?? { borderRadius: 8 }}>
<Component {...pageProps} />
</ConfigProvider>
);
}
需要用户专属首屏主题时,在页面的 getServerSideProps 中读取偏好并返回 initialTheme。客户端首次渲染使用同样的 pageProps;后续切换可以参照上方 Providers 的 React 状态写法。
其他 React SSR 框架
Remix、React Router 框架模式或自建 React SSR 服务使用相同方式:
- 按框架规则在根入口或根路由引入
styles.css,确保样式随首屏加载。
- 在服务端读取主题和语言,把 ConfigProvider 包裹进要渲染的组件树。
- 将同一份初始配置通过框架的数据机制传给客户端。
- 客户端从这份配置初始化状态,再响应主题切换。
静态 CSS 可以由浏览器缓存,动态配置保留在 HTML 的区域容器中。主题计算不依赖 window、matchMedia 或读取 DOM。
保持首次渲染一致
首屏主题优先来自服务端可读的 Cookie、账户设置或固定默认值。若只把偏好放在 localStorage,服务端无法读取:先用一致的默认配置完成接管,再在 effect 中读取偏好,会产生一次可见主题切换。希望首屏直接正确时,应把偏好同步到服务端可读的存储。
日期选择器使用本地日期和时间;时区不同可能导致服务器与浏览器显示不同的值。SSR 页面使用日期默认值时,让服务端与浏览器采用同一时区策略,或在客户端接管后设置用户本地日期。不要在两次首次渲染中分别调用 new Date() 生成不同初值。
Modal、Confirm、Message 与选择浮层会在客户端挂载后创建 Portal。表单控件本身可以服务端渲染;已打开的弹层在客户端接管后展示。
这些交互组件需要客户端 JavaScript 才能响应操作。样式复用与高级主题配置见定制主题。