CSS-in-JS

警告:需要运行时 JavaScript 的 CSS-in-JS 库目前在服务端组件中不受支持。将 CSS-in-JS 与 React 新特性(如服务端组件和流式渲染)结合使用,需要库作者支持最新版 React,包括 并发渲染

我们正与 React 团队合作开发上游 API,以处理支持 React 服务端组件和流式架构的 CSS 和 JavaScript 资源。

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

以下库正在适配支持中:

须知:我们正在测试不同的 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 或更高版本。首先创建一个新的注册表:

'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
}) {
  // 仅使用延迟初始状态创建一次样式表
  // 参考: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>
}

然后用该注册表包裹你的 根布局

import StyledJsxRegistry from './registry'

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

查看示例

Styled Components

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

首先,在 next.config.js 中启用 styled-components:

next.config.js
module.exports = {
  compiler: {
    styledComponents: true,
  },
}

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

'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
}) {
  // 仅使用延迟初始状态创建一次样式表
  // 参考: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>
  )
}

用样式注册表组件包裹根布局的 children

import StyledComponentsRegistry from './lib/registry'

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

查看示例

须知

  • 在服务端渲染期间,样式将被提取到全局注册表并刷新到 HTML 的 <head> 中。这确保样式规则被放置在可能使用它们的内容之前。未来我们可能会使用 React 的新功能来确定注入样式的位置。
  • 在流式渲染期间,每个块的样式将被收集并附加到现有样式中。客户端水合完成后,styled-components 将像往常一样接管并注入任何进一步的动态样式。
  • 我们特意在树的顶层使用客户端组件作为样式注册表,因为这种方式提取 CSS 规则更高效。它避免了在后续服务端渲染时重新生成样式,并防止它们被发送到服务端组件负载中。
  • 对于需要配置 styled-components 编译的个别属性的高级用例,可阅读我们的 Next.js styled-components API 参考 了解更多。

On this page