Menu

CSS-in-JS

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

Reactチームと協力して、サーバーコンポーネントとストリーミングアーキテクチャをサポートするCSS and JavaScriptアセットを処理するアップストリームAPIに取り組んでいます。

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

現在サポートに取り組んでいるライブラリは以下の通りです:

補足: 様々なCSS-in-JSライブラリをテストしており、React 18の機能および/またはappディレクトリをサポートするライブラリの例を順次追加していく予定です。

サーバーコンポーネントのスタイリングを行う場合は、CSS ModulesまたはCSSファイルを出力する他のソリューション(PostCSSやTailwind CSSなど)の使用をおすすめします。

appでのCSS-in-JSの設定

CSS-in-JSの設定は、次の3つのステップからなるオプトインプロセスです:

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

styled-jsx

クライアントコンポーネントでstyled-jsxを使用するには、v5.1.0が必要です。まず、新しいレジストリを作成します:

app/registry.tsx
TypeScript
'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>
}

次に、レジストリでルートレイアウトをラップします:

app/layout.tsx
TypeScript
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フックを使用して、レジストリに収集されたスタイルをルートレイアウトの<head> HTMLタグに挿入します。

lib/registry.tsx
TypeScript
'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>
  )
}

ルートレイアウトのchildrenをスタイルレジストリコンポーネントでラップします:

app/layout.tsx
TypeScript
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は通常通りに動作し、さらなる動的スタイルを注入します。
  • スタイルレジストリのツリーの最上位でClient Componentを特に使用するのは、このようにCSRルールを抽出する方が効率的だからです。これにより、後続のサーバーレンダリング時にスタイルを再生成することを避け、Server Componentのペイロードで送信されることを防ぎます。
  • styled-componentsのコンパイル時に個々のプロパティを設定する必要がある高度なユースケースでは、詳細についてNext.js styled-components APIリファレンスをお読みください。