跳到主要内容

如何使用 CSS-in-JS 库

警告: 在使用 CSS-in-JS 结合 React 新特性(如服务端组件和流式渲染)时,库的作者需要支持最新版本的 React,包括 并发渲染

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

以下库正在适配中:

温馨提示: 我们正在测试不同的 CSS-in-JS 库,并会为支持 React 18 特性和/或 app 目录的库补充更多示例。

app 目录中配置 CSS-in-JS

配置 CSS-in-JS 需要经过三步 opt-in 流程:

  1. 样式注册器:用于在一次渲染中收集所有 CSS 规则。
  2. 使用新的 useServerInsertedHTML hook,在任何可能用到样式的内容之前注入规则。
  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
}) {
// 只在初始时懒加载创建样式表
// 参考: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>
)
}

查看示例

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> 标签中。

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

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

app/layout.tsx
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 规则更高效。它避免了服务端多次渲染时重复生成样式,也防止样式被包含在服务端组件的 payload 中。
  • 如需配置 styled-components 编译的高级属性,请参阅 Next.js styled-components API 参考
示例

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

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

export default HiThere

我们内置了 styled-jsx,用于支持隔离作用域的 CSS。 其目标是支持类似 Web Components 的“Shadow CSS”,但遗憾的是 Web Components 不支持服务端渲染且只能用 JS 实现

其他流行的 CSS-in-JS 方案(如 Styled Components)请参考上方示例。

一个使用 styled-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)中被加载。在开发环境下,为了提供最佳的开发体验(如 Fast Refresh),我们要求启用 JavaScript。