Skip to main content

CSS-in-JS

warning

目前不支持需要运行时 JavaScript 的 CSS-in-JS 库在服务器组件中的使用。 在使用较新的 React 功能,如服务器组件和流式传输时, 使用 CSS-in-JS 需要库作者支持 React 的最新版本,包括并发渲染。

我们正在与 React 团队一起制定处理 CSS 和 JavaScript 资产的上游 API, 以支持 React 服务器组件和流式架构。

以下库在 app 目录中的客户端组件中受支持(按字母顺序排列):

以下库目前正在努力提供支持:

note

我们正在测试不同的 CSS-in-JS 库,将为支持 React 18 特性和/或 app 目录的库添加更多示例。

如果您想要为服务器组件添加样式,我们建议使用 CSS 模块 或其他输出 CSS 文件的解决方案, 例如 PostCSS 或 Tailwind CSS

在 app 中配置 CSS-in-JS

配置 CSS-in-JS 是一个三步选择过程,涉及以下内容:

  1. 样式注册表,用于收集渲染中的所有 CSS 规则。
  2. 新的 useServerInsertedHTML 钩子,在可能使用这些规则的任何内容之前注入规则。
  3. 客户端组件,在初始服务器端渲染期间使用样式注册表包装您的应用程序。

styled-jsx

在客户端组件中使用 styled-jsx 需要使用 v5.1.0。首先,创建一个新的注册表:

app/registry.tsx
'use client'

import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'

export default function StyledJsxRegistry({
children,
}: {
children: React.ReactNode
}) {
// 只使用惰性初始状态创建一次样式表
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [jsxStyleRegistry] = useState(() => createStyleRegistry())

useServerInsertedHTML(() => {
const styles = jsxStyleRegistry.styles()
jsxStyleRegistry.flush()
return <>{styles}</>
})

return <StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>
}

然后,使用注册表包装您的根布局

app/layout.tsx
import StyledJsxRegistry from './registry'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledJsxRegistry>{children}</StyledJsxRegistry>
</body>
</html>
)
}

查看示例 here.

Styled Components

以下是配置 styled-components@6 或更新版本的示例:

首先,使用 styled-components API 创建一个全局注册表组件, 用于在渲染过程中收集生成的所有 CSS 样式规则,并创建一个函数返回这些规则。 然后使用 useServerInsertedHTML 钩子将注册表中收集的样式注入到根布局中的 <head> HTML 标签中。

lib/registry.tsx
'use client'

import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

export default function StyledComponentsRegistry({
children,
}: {
children: React.ReactNode
}) {
// 只使用惰性初始状态创建一次样式表
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())

useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement()
styledComponentsStyleSheet.instance.clearTag()
return <>{styles}</>
})

if (typeof window !== 'undefined') return <>{children}</>

return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
{children}
</StyleSheetManager>
)
}

使用样式注册表组件包装根布局的子元素:

app/layout.tsx
import StyledComponentsRegistry from './lib/registry'

export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
)
}

查看示例 here.

note
  • 在服务器端渲染期间,样式将被提取到全局注册表中,并刷新到 HTML 的 <head> 中。 这确保样式规则位于可能使用它们的任何内容之前。 在将来,我们可能会使用即将推出的 React 特性确定在哪里注入样式。
  • 在流式传输期间,将收集每个块的样式并附加到现有样式。 在客户端混合完成后,styled-components 将像往常一样接管并注入任何进一步的动态样式。
  • 我们特意在树的顶层使用客户端组件来处理样式注册表,因为这样提取 CSS 规则更为高效。 这样可以避免在后续服务器渲染中重新生成样式,并防止它们被发送到服务器组件负载中。