更新: 2026年03月 · 12 分で読める · 5,857 文字
Next.js App Routerで実装する最初のページルーティング
Next.js 13で導入されたApp Routerは、従来のPages Routerに代わる新しいルーティングシステムです。本記事では、App Routerの基本概念から実装まで、すぐにプロジェクトで活用できる実践的なチュートリアルを提供します。
App Routerとは何か
App Routerはファイルシステムベースのルーティングを採用しており、appディレクトリの構造がそのままURLパスとなります。Pages Routerとの最大の違いは、Server Componentsをデフォルトで使用する点と、レイアウト機能が統合されていることです。
Pages Routerではpages/about.jsを作成すれば/aboutルートが生成されますが、App Routerではより柔軟な階層構造と共有レイアウトが可能になります。
プロジェクトのセットアップ
新規プロジェクトの作成
最初に、Next.jsプロジェクトを作成します。以下のコマンドを実行してください。
npx create-next-app@latest my-app --typescript --tailwind --app
セットアップ中に表示される質問では以下を選択してください:
- TypeScript: Yes
- ESLint: Yes
- Tailwind CSS: Yes
- App Router: Yes
- src/ directory: Yes
セットアップが完了したら、プロジェクトディレクトリに移動して開発サーバーを起動します。
cd my-app
npm run dev
http://localhost:3000にアクセスして、Next.jsのウェルカムページが表示されれば成功です。
基本的なルーティング構造
ファイルシステムの階層構造
App Routerのディレクトリ構造は以下のようになります。各フォルダがURLパスに対応します。
app/
├── layout.tsx # ルートレイアウト(全ページで共有)
├── page.tsx # / ページ
├── about/
│ └── page.tsx # /about ページ
├── blog/
│ ├── layout.tsx # /blog 配下のレイアウト
│ ├── page.tsx # /blog ページ
│ └── [slug]/
│ └── page.tsx # /blog/[slug] 動的ルート
└── dashboard/
├── layout.tsx
├── page.tsx # /dashboard ページ
└── settings/
└── page.tsx # /dashboard/settings ページ
page.tsxファイルがそれぞれのルートの実体であり、ファイルが存在しないパスにアクセスすると404エラーが返されます。
ホームページの実装
まずは基本となるホームページを実装しましょう。app/page.tsxを以下のように編集します。
export default function Home() {
return (
<main className="flex flex-col items-center justify-center min-h-screen">
<h1 className="text-4xl font-bold mb-4">Next.js App Router入門</h1>
<p className="text-gray-600 mb-8">App Routerを使ったルーティング実装</p>
<nav className="flex gap-4">
<a href="/about" className="px-4 py-2 bg-blue-500 text-white rounded">
About
</a>
<a href="/blog" className="px-4 py-2 bg-green-500 text-white rounded">
Blog
</a>
</nav>
</main>
);
}
ナビゲーション実装の実践例
共有レイアウトの作成
App Routerの強力な機能の一つが、ページ間で共有できるレイアウトコンポーネントです。すべてのページで共通するヘッダーやナビゲーションをapp/layout.tsxに配置します。
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "My App",
description: "Next.js App Router チュートリアル",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="ja">
<body className="bg-gray-50">
<header className="bg-white shadow">
<nav className="max-w-6xl mx-auto px-4 py-4 flex justify-between items-center">
<a href="/" className="text-2xl font-bold text-blue-600">
MyBlog
</a>
<ul className="flex gap-6">
<li><a href="/about" className="hover:text-blue-600">About</a></li>
<li><a href="/blog" className="hover:text-blue-600">Blog</a></li>
<li><a href="/contact" className="hover:text-blue-600">Contact</a></li>
</ul>
</nav>
</header>
<main className="max-w-6xl mx-auto px-4 py-8">
{children}
</main>
<footer className="bg-gray-800 text-white mt-12 py-6">
<p className="text-center">© 2024 MyBlog. All rights reserved.</p>
</footer>
</body>
</html>
);
}
複数ページの追加
次に、aboutページを追加します。app/about/page.tsxを作成してください。
export default function About() {
return (
<div className="py-8">
<h1 className="text-3xl font-bold mb-4">About Us</h1>
<p className="text-gray-600 mb-4">
このアプリケーションはNext.js App Routerの学習用チュートリアルです。
</p>
<section className="mt-8">
<h2 className="text-xl font-semibold mb-2">特徴</h2>
<ul className="list-disc list-inside space-y-2 text-gray-600">
<li>モダンなルーティングシステム</li>
<li>Server Componentsのデフォルト利用</li>
<li>柔軟なレイアウト構造</li>
</ul>
</section>
</div>
);
}
動的ルートの実装
ブログ記事の個別ページ
ブログのような複数の記事を扱う場合、各記事のURLは異なります。App Routerでは、角括弧を使用して動的セグメントを定義します。
まず、app/blog/page.tsxでブログ一覧を作成します。
const posts = [
{ id: 1, title: "Next.js 入門", slug: "nextjs-intro" },
{ id: 2, title: "React Hooks マスターガイド", slug: "react-hooks" },
{ id: 3, title: "TypeScript の型システム", slug: "typescript-types" },
];
export default function Blog() {
return (
<div>
<h1 className="text-3xl font-bold mb-8">Blog</h1>
<div className="grid gap-6">
{posts.map((post) => (
<article key={post.id} className="border rounded-lg p-6 hover:shadow-lg transition">
<h2 className="text-xl font-semibold mb-2">
<a href={`/blog/${post.slug}`} className="text-blue-600 hover:underline">
{post.title}
</a>
</h2>
<p className="text-gray-600">
投稿記事の要約がここに表示されます。
</p>
</article>
))}
</div>
</div>
);
}
次に、動的ルート用のディレクトリを作成します。app/blog/[slug]/page.tsxを以下のように実装してください。
interface Props {
params: {
slug: string;
};
}
const postContent: Record<string, { title: string; content: string }> = {
"nextjs-intro": {
title: "Next.js 入門",
content: "Next.jsはReactの上に構築されたフルスタックフレームワークです。本記事では基本から応用まで解説します。",
},
"react-hooks": {
title: "React Hooks マスターガイド",
content: "useStateやuseEffectなどのHooksを使いこなす方法を詳しく紹介します。",
},
"typescript-types": {
title: "TypeScript の型システム",
content: "型安全なコード開発を実現するTypeScriptの型システムについて学びます。",
},
};
export default function BlogPost({ params }: Props) {
const post = postContent[params.slug];
if (!post) {
return <div className="text-center py-12"><h1>記事が見つかりません</h1></div>;
}
return (
<article className="max-w-2xl">
<h1 className="text-4xl font-bold mb-4">{post.title}</h1>
<div className="text-gray-600 mb-8">
投稿日: 2024年1月15日 | 読了時間: 5分
</div>
<div className="prose prose-sm max-w-none">
<p>{post.content}</p>
<p>
これは動的ルーティングの例です。URLの{`[slug]`}部分が、paramsオブジェクトを通じてコンポーネントに渡されます。
</p>
</div>
<a href="/blog" className="mt-8 inline-block text-blue-600 hover:underline">
← ブログ一覧に戻る
</a>
</article>
);
}
よくあるハマりポイント
params の型定義の落とし穴
動的ルートのparamsの型定義を誤ると、TypeScriptエラーが発生します。特にNext.js 14以降では、Propsインターフェースを正確に定義する必要があります。
// ❌ 間違った例
export default function BlogPost({ params }) {
// paramsの型が any になるため、型チェックが機能しない
}
// ✅ 正しい例
interface Props {
params: {
slug: string;
};
}
export default function BlogPost({ params }: Props) {
// 型安全にアクセス可能
}
ファイルベースルーティングの順序
複数の動的ルートがある場合、ファイルの順序に注意が必要です。app/blog/[slug]/page.tsxとapp/blog/new/page.tsxの両方がある場合、より具体的なルート(new)を先に定義してください。
// app/blog ディレクトリ構造
blog/
├── page.tsx # /blog
├── new/
│ └── page.tsx # /blog/new (より具体的)
└── [slug]/
└── page.tsx # /blog/:slug (より汎用的)
ナビゲーション最適化
おすすめフロントエンドリソース
- MDN Web Docs The most trusted reference for HTML, CSS, and JavaScript.
- React Documentation Official React tutorials and API reference.
- Can I Use Essential tool for checking browser compatibility.