CSS-in-JS
警告: ランタイムJavaScriptを必要とするCSS-in-JSライブラリは、現在サーバーコンポーネントではサポートされていません。サーバーコンポーネントやストリーミングなどの新しいReact機能でCSS-in-JSを使用するには、ライブラリ作者がコンカレントレンダリングを含むReactの最新バージョンをサポートする必要があります。
Reactチームと協力して、ReactサーバーコンポーネントとストリーミングアーキテクチャをサポートするCSSとJavaScriptアセットを処理するための上流APIを開発中です。
以下のライブラリはapp
ディレクトリ内のクライアントコンポーネントでサポートされています(アルファベット順):
chakra-ui
kuma-ui
@mui/material
@mui/joy
pandacss
styled-jsx
styled-components
stylex
tamagui
tss-react
vanilla-extract
現在サポート作業中のライブラリ:
豆知識: 私たちはさまざまなCSS-in-JSライブラリをテストしており、React 18機能や
app
ディレクトリをサポートするライブラリの例を追加予定です。
サーバーコンポーネントのスタイリングには、CSS ModulesやPostCSS、Tailwind CSSなど、CSSファイルを出力するソリューションの使用を推奨します。
app
でのCSS-in-JS設定
CSS-in-JSの設定は、以下の3ステップのオプトインプロセスです:
- レンダリング中のすべてのCSSルールを収集するスタイルレジストリ
- スタイルを使用する可能性のあるコンテンツの前にルールを注入する新しい
useServerInsertedHTML
フック - 初期サーバーサイドレンダリング中にアプリをスタイルレジストリでラップするクライアントコンポーネント
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
}) {
// 遅延初期状態でスタイルシートを一度だけ作成
// 参照: 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 }) {
// 遅延初期状態でスタイルシートを一度だけ作成
// 参照: 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
styled-components@6
以降の設定例:
まず、next.config.js
でstyled-componentsを有効にします。
module.exports = {
compiler: {
styledComponents: true,
},
}
次に、styled-components
APIを使用して、レンダリング中に生成されたすべてのCSSスタイルルールを収集するグローバルレジストリコンポーネントと、それらのルールを返す関数を作成します。そして、useServerInsertedHTML
フックを使用して、レジストリに収集されたスタイルをルートレイアウトの<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
}) {
// 遅延初期状態でスタイルシートを一度だけ作成
// 参照: 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 }) {
// 遅延初期状態でスタイルシートを一度だけ作成
// 参照: 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リファレンスを参照してください。