next/headers を Client Component で使えない
実際に出るエラー
Cookie やヘッダを読む next/headers を、use client を付けた側へ持ち込んだときに出るエラーです。
読む相手がいない
Cookie もヘッダも、サーバーが要求を受け取って初めて読めるものです。use client を付けたファイルはブラウザにも渡るので、そこから読もうとしても読む相手がいません。
だから、実行してみるまでもなく import の時点で止まります。
'use client'
import { cookies } from 'next/headers'
Error: You're importing a module that depends on "next/headers" into a
React Client Component module. This API is only available in Server Components
but one of its parents is marked with "use client", so this module is also a
Client Component.
直接 import していなくても出る
ここが本題です。実機で確かめました。
app/
Panel.tsx 'use client'。next/headers は書いていない
session.ts ここが next/headers を import している
'use client'
import { currentUser } from './session' // ← これだけ
同じエラーになります。use client の印は、そのファイルから import した先へ下向きに広がるからです。session.ts も Client Component 側の一部になります。
サーバー側に置くもの
cookies() / headers()
DB への問い合わせ
秘密の値
use client から下
Panel.tsx
session.ts(import したので巻き込まれる)
越えられる: 読んだ結果の文字列
越えられない: next/headers そのもの
越えられない: 読む関数ごと渡すこと
直し方
読むのはサーバー、使うのはブラウザに分けます。越えるのは値だけです。
// app/page.tsx(サーバー側のまま)
import { cookies } from 'next/headers'
import { Panel } from './Panel'
export default async function Page() {
const c = await cookies()
return <Panel name={c.get('user')?.value ?? 'ゲスト'} />
}
// app/Panel.tsx
'use client'
export function Panel({ name }: { name: string }) {
return <button onClick={() => alert(name)}>ようこそ {name} さん</button>
}
実機で確かめました。これでビルドは通ります。
向きが逆の、同じ形のエラー
| 持ち込んだもの | 持ち込んだ先 | 出るエラー |
|---|---|---|
| next/headers | use client の側 | このページ |
| useState | Server Component の側 | useState を import できないエラー |
言っていることは1つです。境界をまたいで持ち込めるのは値だけで、仕組みそのものは越えられません。もう片方はuseState を import できないエラーにあります。
共通の関数ほど、両側から読まれる
session.ts のような共通の置き場は、いつのまにか両側から読まれます。そのときに気づけるよう、サーバー専用のものには印を付けてください。
import 'server-only'
import { cookies } from 'next/headers'
こうしておくと、ブラウザ側から読まれた瞬間にビルドが止まります。詳しくはserver-only と client-only の使い分けにあります。
出典
- nextjs.org/docs/app/api-reference/functions/cookies
- nextjs.org/docs/app/api-reference/directives/use-client
このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。