Next.js App Router完全ガイド – Pages Routerからの移行と新機能の使い方
はじめに
Next.js 13で導入されたApp Routerは、React Server Componentsを活用した新しいルーティングシステムです。従来のPages Routerから大幅に改良され、より直感的で高性能なWebアプリケーション開発が可能になりました。この記事では、App Routerの基本概念から実践的な使い方まで詳しく解説します。
App Routerとは
App Router vs Pages Router
App Router(新方式)
- React Server Components対応
- ファイルベースルーティング(app ディレクトリ)
- ネストされたレイアウト
- 改善されたデータフェッチング
Pages Router(従来方式)
- Client Side Rendering中心
- pages ディレクトリベース
- シンプルなルーティング
- getServerSideProps/getStaticProps
基本的なディレクトリ構造
App Routerのファイル構成
app/
├── layout.js # ルートレイアウト
├── page.js # ホームページ
├── loading.js # ローディングUI
├── error.js # エラーページ
├── not-found.js # 404ページ
└── blog/
├── layout.js # ブログレイアウト
├── page.js # ブログ一覧
└── [slug]/
└── page.js # 個別記事
ルートレイアウトの作成
基本的なレイアウト
// app/layout.js
export const metadata = {
title: 'My App',
description: 'Generated by Next.js',
}
export default function RootLayout({ children }) {
return (
<html lang="ja">
<body>
<header>ヘッダー</header>
<main>{children}</main>
<footer>フッター</footer>
</body>
</html>
)
}
ネストされたレイアウト
// app/blog/layout.js
export default function BlogLayout({ children }) {
return (
<div className="blog-container">
<nav>ブログナビゲーション</nav>
<article>{children}</article>
</div>
)
}
ページコンポーネントの作成
基本的なページ
// app/page.js
export default function HomePage() {
return (
<div>
<h1>ホームページ</h1>
<p>App Routerで作成されたページです</p>
</div>
)
}
動的ルートの実装
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
return (
<div>
<h1>記事: {params.slug}</h1>
<p>動的ルートで生成されたページです</p>
</div>
)
}
export async function generateStaticParams() {
return [{ slug: 'first-post' }, { slug: 'second-post' }]
}
Server ComponentsとClient Components
Server Component(デフォルト)
// app/posts/page.js
async function getPosts() {
const res = await fetch('https://api.example.com/posts')
return res.json()
}
export default async function PostsPage() {
const posts = await getPosts()
return (
<div>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
)
}
Client Component
// app/components/Counter.js
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
)
}
データフェッチングの新しい方法
fetch APIの拡張
// app/news/page.js
async function getNews() {
const res = await fetch('https://api.news.com/articles', {
next: { revalidate: 3600 } // 1時間でキャッシュ更新
})
return res.json()
}
export default async function NewsPage() {
const news = await getNews()
return (
<div>
{news.map(article => (
<div key={article.id}>{article.title}</div>
))}
</div>
)
}
ISRの新しい書き方
// app/products/page.js
export const revalidate = 60 // 60秒でキャッシュ更新
async function getProducts() {
const res = await fetch('https://api.shop.com/products')
return res.json()
}
export default async function ProductsPage() {
const products = await getProducts()
return <div>{/* 商品一覧 */}</div>
}
特殊ファイルの活用
ローディングUI
// app/loading.js
export default function Loading() {
return (
<div className="flex justify-center items-center">
<div className="spinner">読み込み中...</div>
</div>
)
}
エラーハンドリング
// app/error.js
'use client'
export default function Error({ error, reset }) {
return (
<div>
<h2>エラーが発生しました</h2>
<button onClick={() => reset()}>再試行</button>
</div>
)
}
404ページ
// app/not-found.js
export default function NotFound() {
return (
<div>
<h2>ページが見つかりません</h2>
<p>お探しのページは存在しません</p>
</div>
)
}
メタデータの設定
静的メタデータ
// app/about/page.js
export const metadata = {
title: 'About Us',
description: '私たちについて',
}
export default function AboutPage() {
return <div>About Us</div>
}
動的メタデータ
// app/blog/[slug]/page.js
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.image],
},
}
}
Route Handlersの使用
API Routes の新しい書き方
// app/api/users/route.js
export async function GET() {
const users = await getUsers()
return Response.json(users)
}
export async function POST(request) {
const data = await request.json()
const user = await createUser(data)
return Response.json(user, { status: 201 })
}
ストリーミングとSuspense
Suspense境界の活用
// app/dashboard/page.js
import { Suspense } from 'react'
function SlowComponent() {
// 時間のかかる処理
return <div>重いコンポーネント</div>
}
export default function Dashboard() {
return (
<div>
<h1>ダッシュボード</h1>
<Suspense fallback={<div>読み込み中...</div>}>
<SlowComponent />
</Suspense>
</div>
)
}
Pages Routerからの移行
段階的移行の手順
// next.config.js(移行期間中の設定)
module.exports = {
experimental: {
appDir: true // App Routerを有効化
}
}
移行チェックリスト
ファイル移行
- pages → app ディレクトリ
- _app.js → layout.js
- _document.js → layout.js
データフェッチング移行
- getServerSideProps → fetch with cache
- getStaticProps → fetch with revalidate
- getStaticPaths → generateStaticParams
パフォーマンス最適化
React Server Componentsの活用
// Server Component(高速)
async function ServerList() {
const data = await fetchData()
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
)
}
// Client Component(必要な場合のみ)
'use client'
function InteractiveButton() {
return <button onClick={handleClick}>クリック</button>
}
部分的プリレンダリング
// app/shop/page.js
export default function ShopPage() {
return (
<div>
<StaticHeader /> {/* 静的部分 */}
<Suspense fallback={<Loading />}>
<DynamicProducts /> {/* 動的部分 */}
</Suspense>
</div>
)
}
TypeScriptとの組み合わせ
型安全なページコンポーネント
// app/blog/[slug]/page.tsx
interface PageProps {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export default function BlogPost({ params, searchParams }: PageProps) {
return <div>記事: {params.slug}</div>
}
実践的な使用例
eコマースサイトの構成
// app/shop/layout.js
export default function ShopLayout({ children }) {
return (
<div className="shop-layout">
<aside>カテゴリー</aside>
<main>{children}</main>
</div>
)
}
// app/shop/[category]/page.js
export default function CategoryPage({ params }) {
return <div>カテゴリー: {params.category}</div>
}
よくある問題と解決策
Q: “use client”をいつ使う? A: useState、useEffect、イベントハンドラーを使う場合のみ
Q: Server Componentでエラーが出る A: ブラウザAPIの使用やuseStateがないか確認
Q: メタデータが反映されない A: page.jsファイル内でexportしているか確認
まとめ
Next.js App Routerは、React Server Componentsを活用した革新的なルーティングシステムです。従来のPages Routerと比べて、より高性能で開発者体験の優れたアプリケーション開発が可能になります。
段階的な移行を行い、新機能を活用することで、現代的なWebアプリケーションを効率的に構築できます。Server ComponentsとClient Componentsを適切に使い分け、ストリーミングやSuspenseを活用して、ユーザーにとって快適な体験を提供しましょう。
この記事はNext.js 13+のApp Routerに基づいて作成されています。最新の機能については公式ドキュメントもご確認ください。
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座
