本文へ移動
verified onnext@16.3.5react@19
最終検証日 2026-09-26
Next.js道場

Server Function の渡し方

関数は Client Component に渡せませんが、use server を書いた Server Function だけは渡せます。

例外はひとつだけ

Server Component から Client Component へ関数は渡せません。渡すとビルドが落ちます。

// ❌ Event handlers cannot be passed to Client Component props.
<Button onClick={() => save()} />

例外は 'use server' を書いた関数、Server Function だけです。

// ✅ 渡せる
<Button onSave={save} />

書き方は2通り

ファイルの先頭に書く。そのファイルが export する関数すべてが対象になります。Client Component から import できるのはこの形だけです。

// app/actions.ts
'use server'
 
export async function submit(value: string) {
  // サーバーで実行される
}

関数の先頭に書く。その関数だけが対象になります。Server Component の中で、その場で定義するときに使います。

export default function Page() {
  async function save(name: string) {
    'use server'
    await db.user.update({ name })
  }
 
  return <EditPost onSave={save} />
}

その場でしか使わない小さな更新なら後者が読みやすく、複数の画面から呼ぶものや、テストを書きたいものは前者に切り出します。

「サーバーで動かす」という意味ではない

'use server' を「この処理をサーバーで動かす指示」だと読むと、置き場所を間違えます。

Server Component はもともとサーバーで動いています。書く必要はありません。

'use server' の意味は、サーバーにある関数を、クライアントから呼べるようにすることです。境界に穴を1つ開ける操作だと考えてください。

'use client' と対になっているように見えますが、役割は対称ではありません。

2つの指示。名前は似ているが役割は逆
書くもの何をする指示か書く場所
use clientここから先はブラウザにも送るファイルの先頭
use serverここをクライアントから呼べる入口にするファイルか関数の先頭
何も書かないサーバーだけで動かす(既定)—
指示意味
'use client'ここから先はブラウザにも送る
'use server'ここをクライアントから呼べる入口にする

中身は届かない。呼び出しは届く

Server Function を渡したとき、境界を越えるのは参照だけ
サーバーに残るもの
関数の中身
db への問い合わせ
認証の判定
ブラウザに届くもの
どこを呼べばよいかの参照
引数と戻り値(通信の中身)

越えられる: 呼び出すための参照

越えられない: 関数のコードそのもの

Server Function を props で渡したとき、ブラウザに届くのはどこを呼べばよいかという参照だけです。関数の中のコードは届きません。

実際に、中に特徴的な文字列を入れた Server Function を Client Component へ渡してビルドし、成果物を検査しました。HTML にも RSC ペイロードにも、配信される JavaScript にも、その文字列は含まれていませんでした。

ボタンを押してから、保存が終わるまで
  1. Browser
    ボタンが押される

    手元にあるのは、どこを呼べばよいかという参照だけ。

  2. Server
    サーバー側の関数が呼ばれる

    db への問い合わせも認証の判定も、すべてここで起きる。

  3. Browser
    戻り値だけが返る

    返した値はそのままブラウザへ渡る。絞り込むのは関数の責任。

ただし、見えないのはコードであって、通信ではありません。

開発者ツールのネットワークタブを開けば、呼び出しの引数と戻り値はそのまま観察できます。参照が分かれば、画面を経由せずに呼ぶこともできます。

だから、次の3つは関数の中で必ず行います。

  1. 認証と権限の確認。公式ドキュメントも、トークンを引数で受け取らず Cookie やヘッダーから読むよう案内しています
  2. 引数の検証。クライアントから来た値は、形も範囲も信用しない
  3. 戻り値を絞る。データベースの行をそのまま返さない
'use server'
 
export async function createUser(data: { name: string }) {
  const session = await auth()
  if (!session?.user) throw new Error('Unauthorized')
 
  const user = await db.user.create({ data })
  return { id: user.id, name: user.name }   // 行をそのまま返さない
}

戻り値はシリアライズされてクライアントへ送られます。渡せる値の制限はServer から Client へ渡せる値と同じです。

定数を同じファイルに置かない

'use server' を書いたファイルから export できるのは非同期関数だけです。async を付け忘れたときの文言と直し方はServer Actions must be async の直し方にあります。

'use server'
 
export const CATEGORIES = ['bug', 'question']   // ❌
A "use server" file can only export async functions, found object.

これは export したものがすべて「クライアントから呼べる入口」として登録されるためで、呼べないものを置くことはできません。

定数は別のファイルに移します。このサイトを作っているときも同じ失敗をして、問い合わせ画面のビルドが落ちました。型だけの export は消えるので、type や interface は同じファイルに残して構いません。

まとめ

  • 関数は渡せない。Server Function だけが例外
  • 'use server' は「サーバーで動かす」ではなく「クライアントから呼べるようにする」
  • 中身は届かないが、呼び出しは誰でもできる。認証・検証・戻り値の制限は関数の中で
  • 定数は別ファイルへ

開けた穴をクライアントからどう呼ぶかはServer Function の呼び方にあります。境界を越えられない関数の話はEvent handlers cannot be passedに、関数を渡さずに済ませる形はClient Component の中に Server Component を置くにあります。

出典

このページの内容は上記の一次情報を基準に書いています。個人ブログや二次情報を根拠にしていません。

理解できたか試す

では、このコードはどうなりますか。

このファイルを別のコンポーネントから import してビルドすると、どうなりますか。

この問題を解くこのテーマの問題 3 問