Next.js 15 完全ガイド — App Router・Server Actions・最新機能まとめ
Next.js 15が2024年10月にリリースされ、2026年現在ではReactフレームワークのデファクトスタンダードとして完全に定着しました。この記事では、Next.js 15の主要機能を実践的なコード例とともに完全解説します。
Next.js 15の主要アップデート
主な変更点
- React 19 サポート - React Compilerと完全統合
- Turbopack 安定版 - 開発サーバーが最大76%高速化
- Server Actions 安定版 - フォーム処理が劇的にシンプルに
- Partial Prerendering (PPR) 実験的サポート - 静的と動的の良いとこ取り
- async Request APIs - cookies(), headers()が非同期に
- fetch Cache のデフォルト変更 -
cache: 'no-store'がデフォルト
App Router完全ガイド
Next.js 15ではApp Routerが標準となりました。Pages Routerも使えますが、新規プロジェクトではApp Routerを使いましょう。
ディレクトリ構造
app/
├── layout.tsx # ルートレイアウト
├── page.tsx # トップページ(/)
├── about/
│ └── page.tsx # /about
├── blog/
│ ├── page.tsx # /blog
│ ├── [slug]/
│ │ └── page.tsx # /blog/my-post
│ └── layout.tsx # /blog配下共通レイアウト
├── api/
│ └── users/
│ └── route.ts # API Route
└── error.tsx # エラーページ
page.tsx - ページコンポーネント
// app/blog/page.tsx
export default function BlogPage() {
return (
<div>
<h1>ブログ一覧</h1>
</div>
);
}
// ファイル名がURLになる
// page.tsx → アクセス可能
// BlogList.tsx → アクセス不可(コンポーネント)
layout.tsx - 共通レイアウト
// app/layout.tsx (ルートレイアウト)
import type { Metadata } from 'next';
import './globals.css';
export const metadata: Metadata = {
title: 'My App',
description: 'Created with Next.js 15',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body>
<header>
<nav>Header Navigation</nav>
</header>
<main>{children}</main>
<footer>Footer Content</footer>
</body>
</html>
);
}
レイアウトはネスト可能です。
// app/blog/layout.tsx
export default function BlogLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="blog-container">
<aside>サイドバー</aside>
<div className="content">{children}</div>
</div>
);
}
ダイナミックルート
// app/blog/[slug]/page.tsx
type Props = {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
};
export default async function BlogPost({ params, searchParams }: Props) {
// Next.js 15ではparamsとsearchParamsが非同期に
const { slug } = await params;
const search = await searchParams;
return <h1>記事: {slug}</h1>;
}
// /blog/nextjs-guide → slug = 'nextjs-guide'
// /blog/react-tips?sort=date → slug = 'react-tips', search.sort = 'date'
Catch-all Routes と Optional Catch-all
// app/docs/[...slug]/page.tsx
// /docs/a → params.slug = ['a']
// /docs/a/b → params.slug = ['a', 'b']
// /docs/a/b/c → params.slug = ['a', 'b', 'c']
// app/shop/[[...slug]]/page.tsx (オプショナル)
// /shop → params.slug = undefined
// /shop/electronics → params.slug = ['electronics']
// /shop/electronics/phones → params.slug = ['electronics', 'phones']
Server ComponentsとClient Components
Next.js 15のApp Routerでは、デフォルトですべてのコンポーネントがServer Componentです。
Server Component(デフォルト)
// app/posts/page.tsx
// "use client" 指定なし = Server Component
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store', // Next.js 15のデフォルト
});
return res.json();
}
export default async function PostsPage() {
const posts = await getPosts();
return (
<div>
{posts.map((post: any) => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}
Server Componentのメリット:
- データフェッチがサーバー側で完結
- バンドルサイズが小さくなる(クライアントにJSを送らない)
- SEOに有利
- 環境変数やAPIキーを直接使える
Client Component
// app/components/Counter.tsx
'use client'; // この行が必須
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
Client Componentが必要な場合:
useState,useEffectなどのReact Hooksを使う- ブラウザAPI(
window,localStorage等)を使う - イベントハンドラ(
onClick,onChange等)を使う - カスタムフックを使う
組み合わせのベストプラクティス
// app/dashboard/page.tsx (Server Component)
import ClientCounter from '@/components/Counter'; // Client Component
async function getDashboardData() {
const res = await fetch('https://api.example.com/dashboard');
return res.json();
}
export default async function Dashboard() {
const data = await getDashboardData();
return (
<div>
<h1>Dashboard</h1>
<p>Total Users: {data.totalUsers}</p>
{/* Server ComponentからClient Componentを呼び出すのはOK */}
<ClientCounter />
</div>
);
}
重要: Server ComponentをClient Componentの子として渡すことはできません。
// ❌ これはNG
'use client';
import ServerComponent from './ServerComponent';
export default function ClientComponent() {
return <ServerComponent />; // エラー
}
// ✅ これはOK - childrenとして渡す
'use client';
export default function ClientWrapper({ children }: { children: React.ReactNode }) {
return <div className="wrapper">{children}</div>;
}
// 親(Server Component)から使う
<ClientWrapper>
<ServerComponent />
</ClientWrapper>
Server Actions - フォーム処理の革命
Server Actionsは、クライアントからサーバー側の関数を直接呼び出せる機能です。Next.js 15で正式に安定版となりました。
基本的な使い方
// app/actions/user.ts
'use server'; // Server Actionの宣言
import { revalidatePath } from 'next/cache';
export async function createUser(formData: FormData) {
const name = formData.get('name') as string;
const email = formData.get('email') as string;
// データベースに保存
await db.user.create({
data: { name, email },
});
// キャッシュを再検証
revalidatePath('/users');
return { success: true };
}
フォームから呼び出す
// app/users/new/page.tsx
import { createUser } from '@/app/actions/user';
export default function NewUserPage() {
return (
<form action={createUser}>
<input name="name" type="text" placeholder="名前" required />
<input name="email" type="email" placeholder="メール" required />
<button type="submit">作成</button>
</form>
);
}
JavaScriptなしでも動作します。プログレッシブエンハンスメントの原則に沿った設計です。
useFormStateでローディング状態を管理
// app/users/new/page.tsx
'use client';
import { useFormState, useFormStatus } from 'react-dom';
import { createUser } from '@/app/actions/user';
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? '送信中...' : '作成'}
</button>
);
}
export default function NewUserPage() {
const [state, formAction] = useFormState(createUser, null);
return (
<form action={formAction}>
<input name="name" type="text" placeholder="名前" required />
<input name="email" type="email" placeholder="メール" required />
<SubmitButton />
{state?.error && <p className="error">{state.error}</p>}
</form>
);
}
Server Actionsのバリデーション
zodと組み合わせると型安全なバリデーションができます。
// app/actions/user.ts
'use server';
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(2, '名前は2文字以上必要です'),
email: z.string().email('有効なメールアドレスを入力してください'),
});
export async function createUser(formData: FormData) {
const rawData = {
name: formData.get('name'),
email: formData.get('email'),
};
// バリデーション
const result = UserSchema.safeParse(rawData);
if (!result.success) {
return {
error: result.error.flatten().fieldErrors,
};
}
// データベースに保存
await db.user.create({ data: result.data });
return { success: true };
}
データフェッチの新しいパターン
Next.js 15のfetchデフォルト変更
// Next.js 14以前: デフォルトでキャッシュされる
fetch('https://api.example.com/data'); // cache: 'force-cache'
// Next.js 15: デフォルトでキャッシュされない
fetch('https://api.example.com/data'); // cache: 'no-store'
明示的にキャッシュしたい場合:
// 60秒間キャッシュ
fetch('https://api.example.com/data', {
next: { revalidate: 60 }
});
// 永続的にキャッシュ
fetch('https://api.example.com/data', {
cache: 'force-cache'
});
Parallel Data Fetching
// 直列フェッチ(遅い)
const user = await fetchUser();
const posts = await fetchPosts(); // userの取得を待ってから実行
// 並列フェッチ(速い)
const [user, posts] = await Promise.all([
fetchUser(),
fetchPosts(),
]);
Sequential Data Fetching(意図的な直列)
async function Page() {
// userIdが必要なので、直列で取得
const user = await fetchUser();
const posts = await fetchPostsByUserId(user.id);
return <div>...</div>;
}
Async Request APIs
Next.js 15では、cookies(), headers(), params, searchParamsが非同期APIになりました。
// app/api/route.ts
import { cookies, headers } from 'next/headers';
export async function GET() {
// Next.js 15: awaitが必要
const cookieStore = await cookies();
const token = cookieStore.get('token');
const headersList = await headers();
const userAgent = headersList.get('user-agent');
return Response.json({ token, userAgent });
}
Partial Prerendering (PPR) - 実験的機能
PPRは静的部分と動的部分を同じページで組み合わせられる画期的な機能です。
// next.config.js
export default {
experimental: {
ppr: true,
},
};
// app/product/[id]/page.tsx
import { Suspense } from 'react';
// 静的部分
function ProductLayout() {
return (
<div>
<header>Static Header</header>
<Suspense fallback={<div>Loading...</div>}>
{/* 動的部分 */}
<DynamicContent />
</Suspense>
</div>
);
}
PPRにより、ページの静的部分は即座に表示され、動的部分はストリーミングで後から読み込まれます。
Turbopack - 開発サーバー高速化
# Next.js 15のデフォルト開発サーバー
npm run dev
# Turbopackを使用(next.config.jsで設定可能)
# 最大76%高速化
2026年現在、Turbopackはプロダクションビルドにも対応しています。
// next.config.js
export default {
experimental: {
turbo: {
// Turbopack設定
},
},
};
まとめ
Next.js 15の主要機能をまとめます。
- App Router - ファイルベースルーティングの進化版
- Server Components - デフォルトでサーバー側レンダリング
- Client Components - インタラクティブな部分のみクライアント側に
- Server Actions - フォーム処理がシンプルに
- Async Request APIs - cookies(), headers()が非同期に
- PPR - 静的と動的のハイブリッドレンダリング
- Turbopack - 開発体験の劇的な向上
Next.js 15は、React Server Componentsを完全に活用した次世代のReactフレームワークです。最初は概念の理解に時間がかかるかもしれませんが、一度慣れれば、従来のPages Routerには戻れないほど快適な開発体験が得られます。
新しいプロジェクトを始めるなら、今すぐNext.js 15 + App Routerで始めましょう。