如何使用 CSS-in-JS 库
警告: 在使用 CSS-in-JS 结合 React 新特性(如服务端组件和流式渲染)时,库的作者需要支持最新版本的 React,包括 并发渲染。
以下库已在 app 目录下的客户端组件中获得支持(按  字母顺序排列):
ant-designchakra-ui@fluentui/react-componentskuma-ui@mui/material@mui/joypandacssstyled-jsxstyled-componentsstylextamaguitss-reactvanilla-extract
以下库正在适配中:
温馨提示: 我们正在测试不同的 CSS-in-JS 库,并会为支持 React 18 特性和/或
app目录的库补充更多示例。
在 app 目录中配置 CSS-in-JS
配置 CSS-in-JS 需要经过三步 opt-in 流程:
- 样式注册器:用于在一次渲染中收集所有 CSS 规则。
 - 使用新的 
useServerInsertedHTMLhook,在任何可能用到样式的内容之前注入规则。 - 在服务端渲染初始阶段,用客户端组件包裹你的应用,并挂载样式注册器。
 
styled-jsx
在客户端组件中使用 styled-jsx 需要 v5.1.0 及以上版本。首先,创建一个新的注册器:
- TypeScript
 - JavaScript
 
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
}) {
  // 只在初始时懒加载创建样式表
  // 参考: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/registry.js
'use client'
import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
export default function StyledJsxRegistry({ children }) {
  // 只在初始时懒加载创建样式表
  // 参考: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>
}
然后,用注册器包裹你的根布局:
- TypeScript
 - JavaScript
 
app/layout.tsx
import StyledJsxRegistry from './registry'
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <StyledJsxRegistry>{children}</StyledJsxRegistry>
      </body>
    </html>
  )
}
app/layout.js
import StyledJsxRegistry from './registry'
export default function RootLayout({ children }) {
  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 hook 将注册器收集到的样式注入到根布局的 <head> 标签中。
- TypeScript
 - JavaScript
 
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
}) {
  // 只在初始时懒加载创建样式表
  // 参考: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>
  )
}
lib/registry.js
'use client'
import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
export default function StyledComponentsRegistry({ children }) {
  // 只在初始时懒加载创建样式表
  // 参考: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:
- TypeScript
 - JavaScript
 
app/layout.tsx
import StyledComponentsRegistry from './lib/registry'
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
      </body>
    </html>
  )
}
app/layout.js
import StyledComponentsRegistry from './lib/registry'
export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
      </body>
    </html>
  )
}
查看示例。
温馨提示:
- 服务端渲染时,样式会被提取到全局注册器,并刷新到 HTML 的
 <head>。这样可以确保样式规则在任何内容使用前就已插入。未来我们可能会用 React 新特性来决定样式注入位置。- 流式渲染时,每个分块的样式会被收集并追加到已有样式中。客户端水合完成后,
 styled-components会像往常一样接管并注入后续动态样式。- 我们特意在树的顶层用客户端组件作为样式注册器,因为这样提取 CSS 规则更高效。它避免了服务端多次渲染时重复生成样式,也防止样式被包含在服务端组件的 payload 中。
 - 如需配置 styled-components 编译的高级属性,请参阅 Next.js styled-components API 参考。