CSS-in-JS

警告: ランタイムJavaScriptを必要とするCSS-in-JSライブラリは、現在サーバーコンポーネントではサポートされていません。サーバーコンポーネントやストリーミングなどの新しいReact機能でCSS-in-JSを使用するには、ライブラリ作者がコンカレントレンダリングを含むReactの最新バージョンをサポートする必要があります。

Reactチームと協力して、ReactサーバーコンポーネントとストリーミングアーキテクチャをサポートするCSSとJavaScriptアセットを処理するための上流APIを開発中です。

以下のライブラリはappディレクトリ内のクライアントコンポーネントでサポートされています(アルファベット順):

現在サポート作業中のライブラリ:

豆知識: 私たちはさまざまなCSS-in-JSライブラリをテストしており、React 18機能やappディレクトリをサポートするライブラリの例を追加予定です。

サーバーコンポーネントのスタイリングには、CSS ModulesやPostCSS、Tailwind CSSなど、CSSファイルを出力するソリューションの使用を推奨します。

appでのCSS-in-JS設定

CSS-in-JSの設定は、以下の3ステップのオプトインプロセスです:

  1. レンダリング中のすべてのCSSルールを収集するスタイルレジストリ
  2. スタイルを使用する可能性のあるコンテンツの前にルールを注入する新しいuseServerInsertedHTMLフック
  3. 初期サーバーサイドレンダリング中にアプリをスタイルレジストリでラップするクライアントコンポーネント

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を有効にします。

next.config.js
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リファレンスを参照してください。