Next.js Server Actions チュートリアル:フォーム処理とバリデーションのベストプラクティス
PC の前で、ユーザー登録フォームのコードを見ています。フォルダにはすでに 4 つのファイル——フォームコンポーネント、API Route、型定義、エラー処理——が積み重なっています。シンプルなフォーム送信のために、200 行近いコードを書いたことになります。
もっとシンプルな方法はないのでしょうか。
答えは Server Actions です。Next.js App Router のこの機能なら、フォーム処理フローを 80% 簡素化できます。API Route を書かず、手動 fetch も不要。煩雑な状態管理すらいりません。便利そうですが、「本当に安全なのか」「バリデーションはどうする」「Loading 状態はどう処理する」といった疑問も湧くでしょう。
使い始めの頃、私も同じ不安がありました。数ヶ月使い込み、いくつかの落とし穴を踏んだうえで得た知見を共有します。Next.js Server Actions を使ったフォーム処理の実戦テクニック——基本の送信から Zod バリデーション、セキュリティ対策、UX 最適化まで、この記事では実際のコード例で素早く習得できます。
Server Actions の基礎
Server Actions とは
Server Actions はサーバーで実行される非同期関数です。'use server' でマークすれば、フォームの action 属性に直接渡せます。フォーム送信時に自動で呼び出され、データ処理、DB 操作、キャッシュ更新——すべてサーバー側で完結します。
主な特徴:
- 型安全:TypeScript が全体のチェーンを検証
- ゼロ設定:
/apiフォルダを作る必要なし - 自動処理:FormData が自動で渡される
書き方は 2 通り。Server Action をコンポーネント内に直接書く(インライン)か、別ファイルに置く(モジュールレベル)かです:
// 方式1:コンポーネント内にインライン
export default function Page() {
async function createUser(formData: FormData) {
'use server' // Server Action としてマーク
const name = formData.get('name')
// データ処理...
}
return <form action={createUser}>...</form>
}
// 方式2:独立ファイル(推奨)
// app/actions.ts
'use server' // ファイルレベルマーク
export async function createUser(formData: FormData) {
const name = formData.get('name')
// データ処理...
}
Server Actions と従来の API Routes の違いは?いつどちらを使うべきか?
比較表を整理しました:
| 特性 | Server Actions | API Routes |
|---|---|---|
| 用途 | フォーム送信、データ変更 | RESTful API、外部呼び出し |
| HTTP メソッド | POST のみ | GET/POST/PUT/DELETE など |
| 型安全 | ネイティブに型安全 | 手動で型定義が必要 |
| 呼び出し方 | 関数を直接呼び出し | fetch リクエスト |
| 適した場面 | 内部ロジック、フォーム | 公開 API、サードパーティ連携 |
| コード量 | 少ない | 相対的に多い |
シンプルに言えば:内部は Server Actions、外部は API Routes。自分のアプリ内のフォーム処理なら Server Actions で十分。他システム向け API や GET リクエストが必要なら API Routes を使いましょう。
Vercel 2025 年の調査によると、すでに 63% の開発者が本番環境で Server Actions を使っています。実験的機能ではありません。
"63% の開発者が本番環境で Server Actions を使用"
最初の Server Actions 例
コードで見てみましょう。最もシンプルなログインフォームです:
// app/login/page.tsx
export default function LoginPage() {
async function handleLogin(formData: FormData) {
'use server' // サーバー関数としてマーク
// フォームからデータ取得
const email = formData.get('email') as string
const password = formData.get('password') as string
// ログイン処理(ここでは簡略化)
console.log('ログイン試行:', email)
// 実プロジェクトではユーザー検証、token 生成など
}
return (
<form action={handleLogin}>
<input
type="email"
name="email"
placeholder="メールアドレス"
required
/>
<input
type="password"
name="password"
placeholder="パスワード"
required
/>
<button type="submit">ログイン</button>
</form>
)
}
これだけです。ポイント:
'use server':Next.js にこの関数をサーバーで実行させるformData.get():フィールドのname属性で値を取得action={handleLogin}:フォーム送信時に自動呼び出し
動作:送信ボタンをクリックすると、ページはリロードされず、データがサーバーに直接送られます。従来方式より fetch、useState、エラー処理のコードが大幅に減ります。
ただしこれは最基礎。実プロジェクトではバリデーション、エラー表示、Loading 状態の処理が必要です。続きを見ていきましょう。
フォームバリデーション実践
Zod でフォームバリデーション
クライアントの required 属性だけ?甘すぎます。ユーザーはブラウザの開発者ツールで簡単にバイパスできます。サーバー側バリデーションは必須です。
ここで Zod が活躍します。サーバー側でデータ形式を検証し、問題があれば即座にエラーを返し、不正データの DB 流入を防ぎます。
まず Zod をインストール:
npm install zod
バリデーションルールを定義:
// app/actions.ts
'use server'
import { z } from 'zod'
// バリデーション schema
const SignupSchema = z.object({
name: z.string().min(2, '名前は 2 文字以上'),
email: z.string().email('メール形式が正しくありません'),
password: z.string().min(8, 'パスワードは 8 文字以上'),
})
export async function signup(formData: FormData) {
// FormData からデータ抽出
const rawData = {
name: formData.get('name'),
email: formData.get('email'),
password: formData.get('password'),
}
// データ検証
const result = SignupSchema.safeParse(rawData)
// 検証失敗時はエラーを返す
if (!result.success) {
return {
success: false,
errors: result.error.flatten().fieldErrors, // フィールド単位のエラー
}
}
// 検証通過、ビジネスロジックを処理
const { name, email, password } = result.data
// ユーザー作成、DB 保存など...
console.log('ユーザー作成:', { name, email })
return {
success: true,
message: '登録成功!',
}
}
ポイント:
safeParseは例外を投げない:失敗時{ success: false, error: ... }を返し、エレガントに処理可能flatten().fieldErrors:検証エラーを{ name: ['エラー1'], email: ['エラー2'] }形式に変換、表示しやすい- 構造化データを返す:
successフラグとエラー情報を含み、クライアントが表示方法を決定
ただし、これらのエラーをフォームにどう表示するか。ここで useActionState が必要になります。
バリデーションエラーの表示:useActionState
useActionState は React 19 で導入された Hook(以前は useFormState)で、Server Actions の返却状態を処理するために設計されています。機能:
- サーバー返却データをコンポーネント状態に保存
- ラップされた action 関数を提供
- フォームが送信中かどうかを通知
コードを見てみましょう:
// app/signup/page.tsx
'use client' // Hook 使用にはクライアントコンポーネント
import { useActionState } from 'react'
import { signup } from '@/app/actions'
export default function SignupPage() {
// 初期状態を定義
const initialState = { success: false, errors: {}, message: '' }
// useActionState:Server Action と初期状態を受け取る
const [state, formAction, isPending] = useActionState(signup, initialState)
return (
<form action={formAction}> {/* 元の action の代わりに formAction */}
<div>
<label>名前</label>
<input
type="text"
name="name"
required
/>
{/* フィールドエラーを表示 */}
{state.errors?.name && (
<p className="error">{state.errors.name[0]}</p>
)}
</div>
<div>
<label>メール</label>
<input
type="email"
name="email"
required
/>
{state.errors?.email && (
<p className="error">{state.errors.email[0]}</p>
)}
</div>
<div>
<label>パスワード</label>
<input
type="password"
name="password"
required
/>
{state.errors?.password && (
<p className="error">{state.errors.password[0]}</p>
)}
</div>
<button type="submit" disabled={isPending}>
{isPending ? '送信中...' : '登録'}
</button>
{/* 成功メッセージを表示 */}
{state.success && (
<p className="success">{state.message}</p>
)}
</form>
)
}
フロー:
- ユーザーがフォーム送信 →
signupを呼び出し - サーバー側で検証失敗 →
{ success: false, errors: {...} }を返却 useActionStateが結果をstateに保存- コンポーネント再レンダリング、エラー情報を表示
isPending はフォーム送信中 true、完了後 false。ボタンの無効化や Loading テキスト表示に使えます。
気づいたかもしれませんが、検証失敗後にユーザー入力が消えます。入力を保持するには、返却時に values フィールドを追加し、入力欄に defaultValue を設定します。ここでは省略しますが、useActionState の役割を理解することが重要です:クライアントコンポーネントと Server Actions を接続し、状態管理をシンプルにする。
ユーザー体験の最適化
Loading 状態と二重送信防止
上記では isPending で Loading を表示しましたが、もう 1 つの Hook があります:useFormStatus。この 2 つは混同しやすく、最初は私も戸惑いました。
シンプルに言えば:
useActionStateのisPending:フォームコンポーネント内で使うuseFormStatusのpending:フォームの子コンポーネント(送信ボタンなど)内で使う
useFormStatus には制限があります:<form> の子コンポーネント内でのみ呼び出せ、フォームコンポーネント内では直接使えません。一見面倒ですが、ボタンを独立コンポーネントに切り出して再利用できるメリットがあります。
例:送信ボタンを分離
// components/SubmitButton.tsx
'use client'
import { useFormStatus } from 'react-dom'
export function SubmitButton({ children }: { children: React.ReactNode }) {
const { pending } = useFormStatus() // フォーム送信状態を取得
return (
<button
type="submit"
disabled={pending}
className={pending ? 'loading' : ''}
>
{pending ? '送信中...' : children}
</button>
)
}
フォーム内で使用:
// app/signup/page.tsx
'use client'
import { useActionState } from 'react'
import { signup } from '@/app/actions'
import { SubmitButton } from '@/components/SubmitButton'
export default function SignupPage() {
const [state, formAction] = useActionState(signup, { success: false, errors: {} })
return (
<form action={formAction}>
{/* フォームフィールド... */}
<SubmitButton>登録</SubmitButton> {/* Loading を自動処理 */}
{state.errors?.general && (
<p className="error">{state.errors.general}</p>
)}
</form>
)
}
ボタンの Loading ロジックが完全にカプセル化されます。送信中:
- ボタンが自動で無効化、二重送信を防止
- テキストが「送信中…」に変化
- スピナーアニメーションも追加可能
pending と isPending の違い?
| 特性 | useActionState の isPending | useFormStatus の pending |
|---|---|---|
| 呼び出し位置 | フォームコンポーネント内部 | フォームの子コンポーネント内部 |
| 適した場面 | フォーム全体の状態にアクセスが必要 | 送信状態のみ必要な独立ボタン |
| 柔軟性 | state と pending を同時取得 | pending のみ取得 |
実プロジェクトでは、こう使い分けています:
- フォームロジックが複雑、複数状態を処理 →
useActionState - 汎用送信ボタンを作る →
useFormStatus
プログレッシブエンハンスメント
Server Actions にはプログレッシブエンハンスメントという機能があります。JavaScript が無効でもフォーム送信が動作します。
Server Actions は本質的にブラウザネイティブの <form> 送信を利用しています。JavaScript がある場合、Next.js が送信を横取りして AJAX リクエストに。JavaScript がない場合は従来のフォーム送信にフォールバックします。
実用シーンは正直、多くありません。今どき JavaScript なしで使えるサイトは少ない……。ただしアクセシビリティやクローラー対応の観点では加点要素。何もしなくても Next.js が自動処理します。
セキュリティとベストプラクティス
Server Actions のセキュリティ
最も見落とされがちな部分です。Server Actions がサーバーで動くから自動的に安全——大間違い。
Server Actions は本質的に公開 API エンドポイントです。Next.js が推測しにくい ID を生成しますが、これは「難読化」であって真のセキュリティ対策ではありません。技術に詳しい人なら、ブラウザの開発者ツールでネットワークリクエストを見れば Action ID を見つけ、手動で呼び出せます。
Next.js が提供する組み込み保護:
- CSRF 対策:Server Actions は POST のみ呼び出し可能。Origin と Host ヘッダーの一致を確認。クロスサイトリクエストは拒否
- 安全な Action ID:各 Action に暗号化 ID。総当たり攻撃が困難
- クロージャ変数の暗号化:Action 内で外部変数を使う場合、Next.js が暗号化
これだけでは不十分。必ず以下を実施:
1. 入力検証
クライアントデータを決して信頼しない。前述の Zod バリデーションは必須です。
2. 認証
ユーザーがログインしているか確認。権限が必要な Action すべてで認証を検証。
3. 認可
ログイン ≠ 権限あり。例:ユーザー A がユーザー B のデータを削除できない。操作権限を検証。
実例:
// app/actions.ts
'use server'
import { cookies } from 'next/headers'
import { z } from 'zod'
const DeletePostSchema = z.object({
postId: z.string().min(1),
})
export async function deletePost(formData: FormData) {
// 1. 入力検証
const rawData = {
postId: formData.get('postId'),
}
const result = DeletePostSchema.safeParse(rawData)
if (!result.success) {
return { success: false, error: '無効なリクエスト' }
}
const { postId } = result.data
// 2. 認証:ログイン確認
const cookieStore = await cookies()
const sessionToken = cookieStore.get('session')?.value
if (!sessionToken) {
return { success: false, error: '先にログインしてください' }
}
// 3. 現在のユーザーを取得
const currentUser = await getUserFromSession(sessionToken)
if (!currentUser) {
return { success: false, error: 'セッションが期限切れです' }
}
// 4. 認可:記事が現在のユーザー所有か確認
const post = await getPost(postId)
if (!post) {
return { success: false, error: '記事が存在しません' }
}
if (post.authorId !== currentUser.id) {
return { success: false, error: 'この記事を削除する権限がありません' }
}
// 5. 操作実行
await deletePostFromDB(postId)
return { success: true, message: '削除成功' }
}
この例は完全なセキュリティチェックフローを示しています:入力検証 → 認証 → 認可 → 操作実行。どれも欠かせません。
おすすめツール:next-safe-action ライブラリ。ミドルウェア機構でバリデーション、認証、エラー処理を統一:
import { createSafeActionClient } from 'next-safe-action'
// 認証付き action クライアントを作成
const actionClient = createSafeActionClient({
// ミドルウェア:ログイン状態を確認
async middleware() {
const session = await getSession()
if (!session) {
throw new Error('未ログイン')
}
return { userId: session.userId }
},
})
// 使用時に認証チェックが自動適用
export const deletePost = actionClient
.schema(DeletePostSchema)
.action(async ({ parsedInput, ctx }) => {
const { postId } = parsedInput
const { userId } = ctx // ミドルウェアからユーザー ID を取得
// 削除実行...
})
認証が必要な Action すべてで同じロジックを再利用でき、コードがすっきりします。
覚えておいてください:Server Actions は魔法ではなく、API エンドポイントです。必要なセキュリティ対策はすべて実施してください。
実戦例:認証付きフォーム
完全な例——ログインユーザーのみが送信できるコメントフォーム:
// app/actions.ts
'use server'
import { cookies } from 'next/headers'
import { z } from 'zod'
import { revalidatePath } from 'next/cache'
const CommentSchema = z.object({
postId: z.string(),
content: z.string().min(1, 'コメントは空にできません').max(500, 'コメントは 500 文字以内'),
})
export async function addComment(formData: FormData) {
// 1. 入力検証
const rawData = {
postId: formData.get('postId'),
content: formData.get('content'),
}
const result = CommentSchema.safeParse(rawData)
if (!result.success) {
return {
success: false,
errors: result.error.flatten().fieldErrors,
}
}
// 2. 認証
const cookieStore = await cookies()
const sessionToken = cookieStore.get('session')?.value
if (!sessionToken) {
return {
success: false,
error: 'コメントするには先にログインしてください',
}
}
const user = await getUserFromSession(sessionToken)
if (!user) {
return {
success: false,
error: 'セッションが期限切れです。再ログインしてください',
}
}
// 3. コメント保存
const { postId, content } = result.data
await saveComment({
postId,
content,
authorId: user.id,
authorName: user.name,
createdAt: new Date(),
})
// 4. ページキャッシュを再検証、コメントを即座に表示
revalidatePath(`/posts/${postId}`)
return {
success: true,
message: 'コメント成功',
}
}
クライアントコンポーネント:
// app/posts/[id]/CommentForm.tsx
'use client'
import { useActionState } from 'react'
import { addComment } from '@/app/actions'
import { SubmitButton } from '@/components/SubmitButton'
export function CommentForm({ postId }: { postId: string }) {
const [state, formAction] = useActionState(addComment, {
success: false,
errors: {},
})
return (
<form action={formAction}>
{/* 非表示フィールドで postId を渡す */}
<input type="hidden" name="postId" value={postId} />
<textarea
name="content"
placeholder="コメントを書く..."
rows={4}
required
/>
{state.errors?.content && (
<p className="error">{state.errors.content[0]}</p>
)}
{state.error && (
<p className="error">{state.error}</p>
)}
{state.success && (
<p className="success">{state.message}</p>
)}
<SubmitButton>コメントを投稿</SubmitButton>
</form>
)
}
この例は前述のすべてのポイントを組み合わせています:
- Zod 入力検証
- ログイン状態確認
useActionStateで状態管理revalidatePathでキャッシュ更新- Loading 状態付き送信ボタン
本番で使える完全なフォーム処理フローです。
応用テクニック
追加パラメータの渡し方
フォームフィールド以外のパラメータを渡す必要がある場合があります。記事編集時にフォーム内容に加えて記事 ID を渡す、など。
1 つの方法は非表示フィールド:
<input type="hidden" name="postId" value={postId} />
よりエレガントな方法:JavaScript の bind メソッド。
// app/actions.ts
'use server'
export async function updatePost(postId: string, formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
// 記事更新...
await updatePostInDB(postId, { title, content })
return { success: true }
}
クライアント側:
// app/posts/[id]/edit/page.tsx
'use client'
import { updatePost } from '@/app/actions'
export default function EditPost({ postId }: { postId: string }) {
// bind で postId パラメータを固定
const updatePostWithId = updatePost.bind(null, postId)
return (
<form action={updatePostWithId}>
<input type="text" name="title" required />
<textarea name="content" required />
<button type="submit">更新</button>
</form>
)
}
bind(null, postId) は新しい関数を作成し、postId を第 1 引数として固定します。フォーム送信時、FormData が第 2 引数として渡されます。
編集、削除など ID が必要な操作に適しています。
データ再検証
Server Actions でデータ処理後、関連ページのキャッシュが古くなっている可能性があります。Next.js は 2 つの関数でキャッシュを更新できます:
1. revalidatePath
パス単位で更新:
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
// 記事作成...
// トップページの記事一覧を更新
revalidatePath('/')
// 記事詳細ページを更新
revalidatePath(`/posts/${newPostId}`)
return { success: true }
}
2. revalidateTag
タグ単位で更新(fetch 時にタグ付けが必要):
// データ取得時にタグ付け
fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }
})
// Server Action 内で 'posts' タグのキャッシュをすべて更新
import { revalidateTag } from 'next/cache'
export async function createPost(formData: FormData) {
// 記事作成...
revalidateTag('posts') // 関連キャッシュをすべて更新
return { success: true }
}
いつどちらを使う?
- パスが固定で数が少ない →
revalidatePath - データが複数ページに分散 →
revalidateTag
私は通常 revalidatePath を優先します。シンプルで直接的。1 つの操作が多数のページに影響する場合のみタグを検討します。
楽観的更新
ほぼ失敗しない操作——いいね、お気に入りなど——には楽観的更新が有効です。UI 上で先に成功を表示し、バックグラウンドで送信します。
React 19 は useOptimistic Hook を提供:
'use client'
import { useOptimistic } from 'react'
import { likePost } from '@/app/actions'
export function LikeButton({ postId, initialLikes }: { postId: string; initialLikes: number }) {
const [optimisticLikes, setOptimisticLikes] = useOptimistic(initialLikes)
async function handleLike() {
// UI を即座に更新(楽観的)
setOptimisticLikes(optimisticLikes + 1)
// バックグラウンドで送信
await likePost(postId)
}
return (
<button onClick={handleLike}>
👍 {optimisticLikes}
</button>
)
}
ボタンクリックで数字が即座に +1。サーバー応答を待つ必要なし。体験が滑らか。
ただし、成功率が非常に高い操作にのみ使ってください。失敗時の UI ロールバックが面倒になります。
まとめ
3 点に絞ります:
-
Server Actions はフォーム処理を簡素化しますが、万能ではありません。内部フォームには使い、外部 API には Route Handlers。すべて Server Actions に統一しないでください。
-
セキュリティは自分で担保。フレームワークが提供するのは基本対策のみ。入力検証、認証、認可チェック——必要なものはすべて実施。Next.js にすべて任せないでください。
-
UX の細部が重要。Loading 状態、エラー表示、楽観的更新——これらの小さな点が、アプリが「まあまあ」か「本当に使いやすい」かを決めます。
useActionStateとuseFormStatusを組み合わせて、すべてをきちんと処理しましょう。
最もシンプルなフォームから試してみてください。Server Action を作り、Zod バリデーションを追加し、Loading を表示——これで 80% の使い方をマスターできます。残り 20%(キャッシュ更新、楽観的更新など)は必要になったときに公式ドキュメントを参照。
Next.js と React は急速に進化しており、Server Actions の API も変わる可能性があります。公式ドキュメントの更新をフォローし、この記事のコードがすぐに古くならないよう注意してください。
さあ、プロジェクトで試してみましょう。次にフォーム送信を書くとき、こんなにシンプルでよかった、と気づくかもしれません。
Server Actions でフォームを処理する完全フロー
Server Action の作成からバリデーション追加、状態管理までの全ステップ
⏱️ 目安時間: 30 分
- 1
ステップ1: Server Action を作成する
app/actions.ts で Server Action を作成:
1. ファイルレベルマーク:ファイル先頭に 'use server' を追加
2. 関数定義:export async function actionName(formData: FormData)
3. データ取得:formData.get('fieldName') でフィールド値を取得
4. 結果を返す:{ success: boolean, errors?: {}, message?: string } 形式で返す
例:
```typescript
'use server'
export async function signup(formData: FormData) {
const name = formData.get('name') as string
// 処理ロジック...
return { success: true, message: '登録成功' }
}
``` - 2
ステップ2: Zod バリデーションを追加する
Zod でサーバー側データ検証:
1. Zod をインストール:npm install zod
2. Schema 定義:const SignupSchema = z.object({ name: z.string().min(2), email: z.string().email() })
3. データ検証:const result = SignupSchema.safeParse(rawData)
4. エラー処理:if (!result.success) return { success: false, errors: result.error.flatten().fieldErrors }
ポイント:
• safeParse は例外を投げず、{ success, data/error } を返す
• flatten().fieldErrors で { field: ['error1'] } 形式に変換
• 検証失敗時は構造化エラーを返し、クライアントで表示 - 3
ステップ3: useActionState で状態を管理する
クライアントコンポーネントで useActionState を使用:
1. Hook をインポート:import { useActionState } from 'react'
2. 初期状態を定義:const initialState = { success: false, errors: {} }
3. Hook を使用:const [state, formAction, isPending] = useActionState(action, initialState)
4. フォームにバインド:<form action={formAction}>
5. エラー表示:{state.errors?.field && <p>{state.errors.field[0]}</p>}
6. Loading 表示:<button disabled={isPending}>{isPending ? '送信中...' : '送信'}</button>
フロー:
• ユーザー送信 → action 呼び出し → 結果返却 → state 更新 → コンポーネント再レンダリング - 4
ステップ4: 認証と認可チェックを追加する
Server Action にセキュリティチェックを追加:
1. 入力検証:Zod で全入力を検証
2. 認証:session token を確認
```typescript
const cookieStore = await cookies()
const sessionToken = cookieStore.get('session')?.value
if (!sessionToken) return { success: false, error: '先にログインしてください' }
```
3. 認可:操作権限を確認
```typescript
const post = await getPost(postId)
if (post.authorId !== currentUser.id) {
return { success: false, error: '権限がありません' }
}
```
4. 操作実行:検証通過後にビジネスロジックを実行
覚えておくこと:Server Actions は魔法ではなく、手動でセキュリティチェックが必要 - 5
ステップ5: ユーザー体験を最適化する
Loading 状態とエラー処理を追加:
1. useFormStatus(ボタンコンポーネント内):
```typescript
'use client'
import { useFormStatus } from 'react-dom'
export function SubmitButton() {
const { pending } = useFormStatus()
return <button disabled={pending}>...</button>
}
```
2. revalidatePath でキャッシュを更新:
```typescript
import { revalidatePath } from 'next/cache'
revalidatePath('/posts')
```
3. 楽観的更新(任意、成功率の高い操作向け):
```typescript
const [optimisticState, setOptimisticState] = useOptimistic(initialState)
```
ベストプラクティス:
• フォームロジックが複雑 → useActionState
• 独立したボタンコンポーネント → useFormStatus
• 操作成功後 → 関連ページのキャッシュを更新
FAQ
Server Actions と API Routes の違いは?いつどちらを使う?
Server Actions は安全?どんな対策が必要?
useActionState と useFormStatus の違いは?
フォームフィールド以外のパラメータはどう渡す?
フォーム送信後にページデータを更新するには?
いつ楽観的更新を使う?
5分で読めます · 公開日: 2025年12月19日 · 更新日: 2026年6月8日
Next.js 完全ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Next.js SSR vs SSG vs ISR:レンダリング戦略の選び方ガイド
Next.js で SSR・SSG・ISR のどれを使うべきか迷っていませんか? 実践シナリオの比較と判断フローで最適なレンダリング戦略を素早く選べます。ISR が効かない、初期表示が遅いといったよくある問題の解決策も紹介します。
第 12 / 47 記事
次の記事
Next.js ルート保護と権限管理:Middleware と多層防御の完全ガイド
Next.js のルート保護と権限管理を深く解析。Middleware から多層防御アーキテクチャまで、NextAuth と getServerSession を組み合わせた安全な RBAC システムの実装を、完全なコード例とともに解説します。
第 14 / 47 記事
関連記事
Next.js App Router 入門ガイド:コア概念と基本操作を解説
Next.js App Router 入門ガイド:コア概念と基本操作を解説
Next.js 15 実践:週末で本番級ブログシステムを構築した方法
Next.js 15 実践:週末で本番級ブログシステムを構築した方法
Next.js Middleware 実践ガイド:パスマッチ、Edge Runtime 制限とよくある落とし穴
コメント
GitHubアカウントでログインしてコメントできます