Next.js 14.2へバージョンアップした際に発生したビルドエラーの解消
前提
お仕事でNext.jsを使用したプロダクトを開発しています。
社内で色々とごたついていたので、バージョンアップが遅れてしまっていましたが、ようやく実施してみるとビルドエラーが発生するようになってしまったので、それらを解消した際の備忘録になります。
今回行ったNext.jsのバージョンアップは14.0.3 -> 14.2.3となり、app routerとTailwind CSSを使用しています。
TL;DR
- SCコンポーネントのexportと、CCであるべきコンポーネントのexportを同じindex.tsに書かない
- Tailwind CSSのimportはRoot Layoutのみで行う
発生したエラー
usePathnameを使用したコンポーネントがClient Componentであるべきと怒られる
エラー内容
./src/components/Parent/Child/Child.tsx
Error:
x You're importing a component that needs usePathname. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
|
|
,-[my-app/src/components/Parent/Child/Child.tsx:1:1]
1 | import { usePathname } from "next/navigation";
: ^^^^^^^^^^^
2 | // import { useState } from "react";
3 |
4 | export function Child() {
`----
Import trace for requested module:
./src/components/Parent/Child/Child.tsx
./src/components/Parent/Child/index.ts
./src/components/Parent/index.ts
./src/app/page.tsx
app router導入時にありがちな、CCでしか使えないよ、というエラーです。
ディレクトリ構成
src/
├── app
│ └── page.tsx
└── components
└── Parent ("use client"を宣言)
├── Child
│ ├── Child.tsx (usePathnameを使用)
│ └── index.ts
├── Parent.tsx
└── index.ts
コンポーネントを呼び出している順序としては
page.tsx -> Parent.tsx -> Child.tsx
のようになっており、usePathnameはChild.tsxで使用しています。
Parent.tsxではuse client
を宣言しているため、ここがSCとCCの境界になっています。
つまり、usePathnameを使用しているChild.tsxは、その親であるParent.tsxにてCCの内側に位置していることになります。
原因
基本的に、use client
を宣言しているコンポーネントから呼び出すコンポーネントは、その中で改めてuse client
を宣言しなくてもCCで扱われます。
実際、バージョンを上げる前では、そのように認識されていたため、エラーが生じていなかったと思われます。
ここで、エラーログを確認するとインポートのトレース情報が出力されています。
Import trace for requested module:
./src/components/Parent/Child/Child.tsx
./src/components/Parent/Child/index.ts
./src/components/Parent/index.ts
./src/app/page.tsx
上記を下の行からみると、下から2行目でParent.tsxのexport文を書いているindex.tsの次に、Child.tsxが参照されてしまっているようです。
すなわち、SCとCCの境界であるParent.tsxを介さずに、直接Child.tsxが参照されてしまってるようです。
このような参照になってしまった原因は、./src/components/Parent/index.tsに、Child.tsxのexport文も併せて書いてしまっているためでした。
export { Parent } from "./Parent";
export { Child } from "./Child";
解消方法
本来、Child.tsxはParent.tsxから参照するコンポーネントなので、上記のindex.tsでのexportは不要となるため、exoport文を削除したところ、ビルドが成功しました。
export { Parent } from "./Parent";
- export { Child } from "./Child";
余談
このエラーは14.1では確認できず、14.2で生じるようになっていますが、この差分が何に起因しているものなのかは追えていないです。
しかし、ビルドが成功する14.1にて、usePathnameではなくuseStateを使用した場合は、14.2と同じくエラーとなります。
このことから、下記のいずれかの要因で生じるようになったのではないかと推察しましたが、憶測の域を出ないです。
(このあたりご存知の方がいらっしゃれば教えていただきたいです。)
- 14.2での最適化の中で、importを参照する処理が変わった。
- 14.1までの時点では見逃されていたusePathnameがCCであるべきチェックが、14.2で正しく行われるようになった
Tailwind CSSに対してSyntaxErrorが発生する
エラー内容
/node_modules/.pnpm/tailwindcss@3.3.3/node_modules/tailwindcss/tailwind.css:1
@tailwind base;
^
SyntaxError: Invalid or unexpected token
at internalCompileFunction (node:internal/vm:73:18)
(以下略)
Error occurred prerendering page "hogehoge". Read more: https://nextjs.org/docs/messages/prerender-error
原因と解消方法
14.2のアップデート内容として、CSSの最適化が挙げられます。
CSSをチャンク化することで、実現しているそうですが、このチャンク化においては、CSSを読み込む順序が重要そうです。
今回、エラーが起きている箇所を限定していったところ、重複してTailwindをインポートしている箇所があることがわかりました。
src/
└── app
├── global.css
├── layout.tsx(Tailwindをインポート)
└── hoge
└── layout.tsx(Tailwindをインポート)
import "tailwindcss/tailwind.css";
(以下略)
Next.jsのドキュメントにも記載されている通り、Tailwindを使用する場合はRoot Layoutでの読み込みが推奨されていたため、該当のインポート文を削除したところエラーが解消されました。
Discussion