警告:将 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 是一个三步选择加入的过程,包括:
useServerInsertedHTML hook,用于在任何可能使用这些规则的内容之前注入规则。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
}) {
// Only create stylesheet once with lazy initial state
// 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>
}'use client'
import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
export default function StyledJsxRegistry({ children }) {
// Only create stylesheet once with lazy initial state
// 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>
}然后,用注册表包裹你的根布局:
import StyledJsxRegistry from './registry'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledJsxRegistry>{children}</StyledJsxRegistry>
</body>
</html>
)
}import StyledJsxRegistry from './registry'
export default function RootLayout({ children }) {
return (
<html>
<body>
<StyledJsxRegistry>{children}</StyledJsxRegistry>
</body>
</html>
)
}以下是如何配置 styled-components@6 或更高版本的示例:
首先,在 next.config.js 中启用 styled-components。
module.exports = {
compiler: {
styledComponents: true,
},
}然后,使用 styled-components API 创建一个全局注册表组件,用于收集渲染期间生成的所有 CSS 样式规则,以及一个返回这些规则的函数。接着使用 useServerInsertedHTML hook 将注册表中收集的样式注入到根布局的 <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
}) {
// Only create stylesheet once with lazy initial state
// 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>
)
}'use client'
import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
export default function StyledComponentsRegistry({ children }) {
// Only create stylesheet once with lazy initial state
// 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:
import StyledComponentsRegistry from './lib/registry'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
)
}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 参考以了解更多信息。
可以使用任何现有的 CSS-in-JS 解决方案。最简单的是内联样式:
function HiThere() {
return <p style={{ color: 'red' }}>hi there</p>
}
export default HiThere我们捆绑了 styled-jsx 来提供对隔离的 scoped CSS 的支持。
其目标是支持类似于 Web Components 的“影子 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,CSS 仍然会在生产构建 (next start) 中加载。在开发过程中,我们要求启用 JavaScript 以提供具有快速刷新的最佳开发体验。