Skip to content

CSS-in-JS

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

我们正在与 React 团队合作开发上游 API,以处理 CSS 和 JavaScript 资源,并支持 React Server Components 和流式架构。

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

以下库目前正在进行支持工作:

须知:我们正在测试不同的 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. 在初始服务器端渲染期间,使用样式注册表包装您的应用程序的客户端组件。### styled-jsx

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

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>
}
jsx
'use client'

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

export default function StyledJsxRegistry({ children }) {
  // 仅使用惰性初始状态创建一次样式表
  // 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>
}

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

tsx
import StyledJsxRegistry from './registry'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <StyledJsxRegistry>{children}</StyledJsxRegistry>
      </body>
    </html>
  )
}
jsx
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。

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

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

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>
  )
}

lib/registry.js

jsx
'use client'

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

export default function StyledComponentsRegistry({ children }) {
  // 仅使用懒惰的初始状态创建一次样式表
  // 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

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

jsx
import StyledComponentsRegistry from './lib/registry'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
      </body>
    </html>
  )
}

查看示例

须知

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

App 标签内容结束。

Pages 标签内容开始。

示例

可以使用任何现有的 CSS-in-JS 解决方案。最简单的一个是内联样式:

jsx
function HiThere() {
  return <p style={{ color: 'red' }}>hi there</p>
}

export default HiThere

Pages 标签内容结束。

集成CSS

我们捆绑了styled-jsx以提供对隔离作用域CSS的支持。 我们的目标是支持类似于Web Components的"shadow CSS",不幸的是它们不支持服务器渲染,并且仅限于JS

请参阅上面的示例,了解其他流行的CSS-in-JS解决方案(如Styled Components)。

使用styled-jsx的组件如下所示:

jsx
function HelloWorld() {
  return (
    <div>
      Hello world
      <p>scoped!</p>
      <style jsx>{`
        p {
          color: blue;
        }
        div {
          background: red;
        }
        @media (max-width: 600px) {
          div {
            background: blue;
          }
        }
      `}</style>
      <style global jsx>{`
        body {
          background: black;
        }
      `}</style>
    </div>
  )
}

export default HelloWorld

有关更多示例,请参见styled-jsx文档

禁用JavaScript

是的,如果您禁用了JavaScript,CSS仍将在生产构建中加载(next start)。在开发过程中,我们需要启用JavaScript,以提供最佳的开发体验,包括Fast Refresh