Next.js の Server Action が実行できない
Server Action は use server ディレクティブが付いた関数のみが対象で、Client Component から呼ぶ場合は別ファイル(先頭に use server)に書く必要がある。
form の action 属性に関数を直接渡し、App Router 環境で利用する。
Pages Router では使えない。
#server-action#app-router#form#use-server
公開:
要約
Server Action が動かない場合、最初に疑うのは "use server" ディレクティブの位置と、Client Component 側からの呼び出し方法。
Client Component の中に inline で書いた async 関数はそのままでは Server Action にならないため、別ファイルに切り出して先頭で "use server" を宣言し、<form action={fn}> か useTransition 経由で呼ぶ。
App Router 専用機能で、Next.js 14 以降では既定で有効。
よくある原因
- Client Component(
"use client"付き)の中にasync function submit() { ... }を書いてonSubmitに渡しているが、これはただのクライアント側関数で Server Action ではない。 button.onClick={fn}から Server Action を直接呼ぼうとしている。
Server Action は<form action={fn}>かstartTransition(() => fn())経由で起動するのが想定。- プロジェクトが Pages Router(
pages/配下)で運用されている。
Server Action は App Router の機能で、Pages Router にはバックポートされていない。 - Next.js 13.4 以前で
experimental.serverActionsフラグを立てていない、またはnext devを再起動していない。
解決策
1. アクションを別ファイルに切り出す
// app/actions.ts
"use server";
export async function createTodo(formData: FormData) {
const title = formData.get("title");
// ... DB 書き込み等
}// app/page.tsx (Server Component)
import { createTodo } from "./actions";
export default function Page() {
return (
<form action={createTodo}>
<input name="title" />
<button type="submit">追加</button>
</form>
);
}ファイル先頭の "use server" で、その module の全 export が Server Action として扱われる(公式ドキュメント)。
2. Client Component から呼ぶ
"use client";
import { useTransition } from "react";
import { createTodo } from "./actions";
export function AddBtn() {
const [pending, start] = useTransition();
return (
<button onClick={() => start(() => createTodo(new FormData()))} disabled={pending}>
追加
</button>
);
}3. App Router に移行する
pages/ を app/ に置き換えるのは段階的でよい。
並走させた上で、Server Action を使う画面だけ app/ で実装する。
4. Next.js を更新する
npm install next@latest14 系以上に上げ、next.config.js に古い experimental.serverActions 設定が残っていれば削除する。
アップデート後は next dev を再起動する。