Menu

How to use CSS-in-JS libraries

警告: 在新版React特性(如Server Components和Streaming)中使用CSS-in-JS需要库作者支持最新版本的React,包括并发渲染

以下库在app目录中的Client Components中受支持(按字母顺序):

以下库正在开发支持:

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

如果你想给Server Components添加样式,我们推荐使用CSS Modules或其他输出CSS文件的解决方案,如PostCSS或Tailwind CSS

app中配置CSS-in-JS

配置CSS-in-JS是一个三步选择性过程,包括:

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

styled-jsx

在Client Components中使用styled-jsx需要使用v5.1.0版本。首先,创建一个新的注册表:

app/registry.tsx
TypeScript
'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
TypeScript
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标签中。

lib/registry.tsx
TypeScript
'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>
  )
}

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

app/layout.tsx
TypeScript
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将像往常一样接管并注入任何进一步的动态样式。
  • 我们专门在树的顶层使用Client Component作为样式注册表,因为这样提取CSS规则更有效。它避免在后续服务器渲染时重新生成样式,并防止它们被发送到Server Component负载中。
  • 对于需要配置styled-components编译的各个属性的高级用例,你可以阅读我们的Next.js styled-components API参考了解更多信息。