ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
この記事の要点
'use server'を付けた非同期関数はサーバーで実行される関数になり、クライアントから直接呼べる。API を自分で作らなくてよい。formのactionに渡すと、FormDataが自動的に引数として届く。- UI を経由せず直接 POST できる。認証と認可の確認は必ず関数の中で行う。
- 送信中の状態は
useActionStateが返すpendingで表現できる。 - 更新後は
updateTagやrevalidatePathでキャッシュを整え、必要ならredirectで遷移する。
従来、ブラウザからデータを更新するには API エンドポイントを作り、fetch で呼び、レスポンスを見て画面を更新する、という手順が必要でした。Next.js の Server Functions はこの往復をフレームワーク側が引き受けます。関数を書いてフォームに渡すだけで、通信も画面更新もつながります。フォーム送信の文脈で使うものを特に Server Actions と呼びます。
1定義する
非同期関数の本体先頭、またはファイルの先頭に 'use server' と書きます。ファイル先頭に書いた場合は、そのファイルのすべてのエクスポートが対象になります。
// app/lib/actions.ts
'use server'
import { auth } from '@/lib/auth'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) {
throw new Error('Unauthorized')
}
const title = formData.get('title')
const content = formData.get('content')
// データを保存する
// キャッシュを整える
}
サーバーコンポーネントの中に直接書くこともできます。小さな処理ならこちらが手軽です。
export default function Page() {
async function createPost(formData: FormData) {
'use server'
// ...
}
return <form action={createPost}>{/* ... */}</form>
}
クライアントコンポーネントの中で定義することはできません。別ファイルに 'use server' を付けて置き、それを import して使います。
2フォームから呼ぶ
もっとも基本的な呼び出し方が、form の action に関数そのものを渡す形です。
import { createPost } from '@/app/lib/actions'
export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<textarea name="content" />
<button type="submit">作成</button>
</form>
)
}
送信すると、name 属性を持つ入力値が FormData にまとめられて関数の引数に渡ります。onSubmit も preventDefault も fetch も書きません。
サーバーコンポーネント上のフォームであれば、JavaScript がまだ読み込まれていない状態でも送信が成立します(プログレッシブエンハンスメント)。クライアントコンポーネント上のフォームの場合は、送信が一時的に待ち行列に入り、ハイドレーション完了後に処理されます。
3イベントハンドラから呼ぶ
フォーム以外からも呼べます。クライアントコンポーネントで import して、通常の非同期関数のように扱います。
'use client'
import { incrementLike } from './actions'
import { useState } from 'react'
export default function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)
return (
<>
<p>いいね: {likes}</p>
<button
onClick={async () => {
const updated = await incrementLike()
setLikes(updated)
}}
>
いいね
</button>
</>
)
}
ただし、クライアントからの呼び出しは 1 つずつ順番に処理されます。並列にデータを取りたい場合は、サーバーコンポーネント側で取得するか、1 つの関数の中でまとめて処理してください。
4送信中の状態を出す
useActionState は、状態・アクション・pending フラグの 3 つを返します。二重送信の防止にも使えます。
'use client'
import { useActionState } from 'react'
import { createPost } from '@/app/actions'
const initialState = { message: '' }
export function Form() {
const [state, formAction, pending] = useActionState(createPost, initialState)
return (
<form action={formAction}>
<input type="text" name="title" required />
<textarea name="content" required />
{state?.message && <p aria-live="polite">{state.message}</p>}
<button disabled={pending}>作成</button>
</form>
)
}
この形にする場合、サーバー側の関数は第 1 引数に前回の状態を受け取る形になります。
'use server'
export async function createPost(prevState: any, formData: FormData) {
const title = formData.get('title')
// 検証に失敗したら「投げる」のではなく「返す」
if (!title) {
return { message: 'タイトルを入力してください' }
}
// ...
}
入力ミスのような想定内の失敗は例外にしないのが原則です。戻り値として表現し、画面にメッセージを出します。例外を投げるとエラー境界に落ち、フォームの入力内容ごと画面が差し替わってしまいます。この考え方は エラーハンドリングとエラー境界 で詳しく扱います。
5更新後の後始末
データを書き換えたら、表示を新しい状態に合わせる必要があります。用途に応じて使い分けます。
| やりたいこと | 使う API |
|---|---|
| 自分の変更を即座に画面へ反映 | updateTag('user-1') |
| 他の閲覧者にも少し遅れて反映 | revalidateTag('posts', 'max') |
| 特定のパスをまとめて作り直す | revalidatePath('/posts') |
| 表示だけを取り直す | refresh() |
| 完了後に別ページへ移動 | redirect('/posts') |
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// 保存処理
revalidatePath('/posts')
redirect('/posts')
}
redirect は制御用の例外を投げる実装になっているため、その後ろのコードは実行されません。キャッシュの無効化は必ず redirect より前に書いてください。
6Cookie を操作する
Server Function の中では cookies() で取得・設定・削除ができます。設定や削除を行うと、現在のページとレイアウトがサーバー側で再描画され、新しい値が反映された状態になります。
'use server'
import { cookies } from 'next/headers'
export async function setTheme(value: string) {
const cookieStore = await cookies()
cookieStore.set('theme', value)
}
7つまずきやすいところ
Server Function は POST で直接叩けます。「管理者にしかボタンを見せていない」は防御になりません。すべての関数の冒頭でログイン状態と、その資源に対する権限を確認してください。
①認可 → ②入力検証 → ③更新 → ④キャッシュ無効化 → ⑤(必要なら)遷移、の順で書くと抜けが出にくくなります。
- クライアントコンポーネントの中で
'use server'を書いてしまう:定義はできません。別ファイルに切り出します。 - props で渡すときの命名:クライアントコンポーネントへ関数を渡す場合、
updateItemActionのように Action で終わる名前にしておくと、リンターの支援を受けやすくなります。 - 戻り値が渡せない:クライアントへ返る値は React がシリアライズできるものに限られます。クラスインスタンスなどはそのまま返せません。
- 複数台構成で「Server Action が見つからない」:インスタンスごとに暗号鍵が変わるためです。共通の鍵を環境変数で指定します(本番ビルドとセルフホスト)。
更新処理が書けるようになったら、失敗したときの振る舞いを設計します。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 Spring Frameworkのアノテーション一覧
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- PHPでのファイルアップロード方法|multipart/form-dataと$_FILES 2026-08-07 17:15:57
- PHP $_SERVERとは|DOCUMENT_ROOT・PHP_SELFなど主な要素 2026-08-07 17:15:57
- Scratchのアカウントの作り方|メールアドレスとユーザー名の登録手順 2026-08-07 17:15:57
- djangoにおけるMVCアプリケーション実装例 2026-08-07 17:15:57
- Bluetoothとは|仕組み・BLEとClassicの違い・用途 2026-08-07 17:15:57
- Spring Bootのテスト入門|@SpringBootTestとJUnitでの単体・結合テスト 2026-08-07 17:15:57
- Java とは?言語仕様・JVM・主要フレームワーク一覧 2026-08-07 17:15:56
- インストール方法(Windows版) 2026-08-07 17:15:56
- APIキー取得方法 2026-08-07 17:15:56
- 【PHPフレームワーク】Laravelの使い方 2026-08-07 17:15:56
- Scratch 作品の公開方法 2026-08-07 17:15:56
- Google OAuth 2.0 認証の実装方法 2026-08-07 17:15:56
- データベースとは?RDB / NoSQL / SQL の基礎 2026-08-07 17:15:56
- インストール(eclipseプラグイン) 2026-08-07 17:15:56
- Spring Frameworkの使い方 2026-08-07 17:15:56
コメントを削除してもよろしいでしょうか?