App RouterでEmotionを使う時の致命的な辛み
Emotionを使っているNext.jsプロジェクトをPages RouterからApp Routerに移行しようとした時に出会った辛みについて書いていきます。
metadataを定義できない問題
App Routerでメタデータ(HTML head要素内のmetaタグやlinkタグなど)を定義する際にはlayoutコンポーネントまたはpageコンポーネントで以下のように設定します。
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}
Emotionを使用していると、このmetadataを設定することができなくなります。Emotionを使用している場合、HTMLタグまたはコンポーネントが書かれているファイル全てをClient Component("use client"
)にする必要があります。しなかった場合には以下のエラーが表示されます。
しかし、metadataはServer Componentにしか定義できず、Client Componentに定義すると以下のようなエラーが表示されます。
そのため、ページ単位でmetadataを定義することができず、metadataを定義するならlayoutコンポーネントのheadに直接定義することになります。しかし、これをすることで全てのページのmetadataが同じになってしまうため、toC向けのようにページ毎にmetadataを変える必要のあるアプリケーションでは致命的な問題となります。
fetchするとwarningが出る問題
App RouterではAPIからデータを取得する際に以下のようにコンポーネントで直接fetchを実行します。
export async function Test() {
const response = await fetch("https://example.com/api");
return <div>{response.status}</div>;
}
しかし、Client Componentでasync/awaitを使うと以下のようなwarningが出ます。
エラーの内容をDeepLで翻訳すると以下の通りです。
コンポーネントがキャッシュされていないプロミスによってサスペンドされました。クライアント・コンポーネントまたはフック内でのプロミスの作成は、サスペンス互換のライブラリまたはフレームワークを使用する場合を除き、まだサポートされていません。
このことからClient Componentまたはフック内でfetchをすることはできないため、Emotionを使っている場合、Server Side Rendering中にAPI通信をすることが難しいようです。
おわりに
今回挙げた問題は自分が軽い気持ちでEmotionとApp Routerを使おうとした時に起きた問題なので回避方法があるかもしれません。もし、ご存知の方はコメントをいただけると嬉しいです🙏
Discussion