Next.js App Routerで始める現代的なWebアプリ開発

Next.js 13で導入されたApp Routerの基本的な使い方から実践的な活用方法まで詳しく解説します。

Next.js App Router で始める現代的な Web アプリ開発

Next.js 13 で導入された App Router は、従来の Pages Router に代わる新しいルーティングシステムです。React Server Components を活用し、より効率的で柔軟な Web アプリケーション開発を可能にします。

App Router とは

App Router は、appディレクトリを使用した新しいルーティングシステムです。ファイルベースのルーティングを維持しながら、より強力な機能を提供します。

主な特徴

  • React Server Components: サーバーサイドでのコンポーネントレンダリング
  • ストリーミング: 段階的なページ読み込み
  • レイアウト: 共通レイアウトの効率的な管理
  • ローディング状態: 組み込みのローディング機能

基本的なディレクトリ構造

app/
├── layout.tsx          # ルートレイアウト
├── page.tsx           # ホームページ
├── loading.tsx        # ローディングUI
├── error.tsx          # エラーUI
├── not-found.tsx      # 404ページ
└── blog/
    ├── layout.tsx     # ブログレイアウト
    ├── page.tsx       # ブログ一覧
    └── [slug]/
        └── page.tsx   # 記事詳細

基本的な実装

ルートレイアウト

// app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body>
        <header>
          <nav>ナビゲーション</nav>
        </header>
        <main>{children}</main>
        <footer>フッター</footer>
      </body>
    </html>
  );
}

ページコンポーネント

// app/page.tsx
export default function HomePage() {
  return (
    <div>
      <h1>ホームページ</h1>
      <p>App Router を使用した Next.js アプリケーション</p>
    </div>
  );
}

動的ルート

// app/blog/[slug]/page.tsx
interface BlogPostProps {
  params: Promise<{ slug: string }>;
}

export default async function BlogPost({ params }: BlogPostProps) {
  const { slug } = await params;

  return (
    <article>
      <h1>記事: {slug}</h1>
    </article>
  );
}

Server Components と Client Components

Server Components(デフォルト)

// サーバーで実行される
async function ServerComponent() {
  const data = await fetch("https://api.example.com/data");
  const posts = await data.json();

  return (
    <div>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  );
}

Client Components

"use client";

import { useState } from "react";

function ClientComponent() {
  const [count, setCount] = useState(0);

  return <button onClick={() => setCount(count + 1)}>カウント: {count}</button>;
}

データフェッチング

fetch API の拡張

// キャッシュされたリクエスト
const data = await fetch("https://api.example.com/posts", {
  cache: "force-cache",
});

// 毎回新しいデータを取得
const data = await fetch("https://api.example.com/posts", {
  cache: "no-store",
});

// 一定時間後に再検証
const data = await fetch("https://api.example.com/posts", {
  next: { revalidate: 60 },
});

静的生成とメタデータ

export async function generateStaticParams() {
  const posts = await fetch("https://api.example.com/posts").then((res) =>
    res.json()
  );

  return posts.map((post) => ({
    slug: post.slug,
  }));
}

export async function generateMetadata({ params }: BlogPostProps) {
  const { slug } = await params;
  const post = await getPost(slug);

  return {
    title: post.title,
    description: post.description,
  };
}

ローディングとエラーハンドリング

ローディング UI

// app/blog/loading.tsx
export default function Loading() {
  return (
    <div className="animate-pulse">
      <div className="h-4 bg-gray-200 rounded w-3/4 mb-4"></div>
      <div className="h-4 bg-gray-200 rounded w-1/2"></div>
    </div>
  );
}

エラーハンドリング

// app/blog/error.tsx
"use client";

export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div>
      <h2>エラーが発生しました</h2>
      <button onClick={() => reset()}>再試行</button>
    </div>
  );
}

パフォーマンス最適化

ストリーミング

import { Suspense } from "react";

export default function Page() {
  return (
    <div>
      <h1>ページタイトル</h1>
      <Suspense fallback={<div>読み込み中...</div>}>
        <SlowComponent />
      </Suspense>
    </div>
  );
}

部分的プリレンダリング

// app/blog/[slug]/page.tsx
export const experimental_ppr = true;

export default async function BlogPost({ params }: BlogPostProps) {
  const { slug } = await params;

  return (
    <article>
      <Suspense fallback={<PostSkeleton />}>
        <PostContent slug={slug} />
      </Suspense>
    </article>
  );
}

まとめ

App Router は、Next.js アプリケーション開発に革新をもたらします。Server Components による効率的なレンダリング、改善されたデータフェッチング、そして直感的なファイル構造により、より良いユーザー体験と開発者体験を実現できます。

従来の Pages Router からの移行は段階的に行うことができるため、既存のプロジェクトでも安心して導入できます。現代的な Web アプリケーション開発において、App Router は必須の技術と言えるでしょう。

最後まで読んでいただきありがとうございました!てばさん(@basabasa8770)でした!

この記事をシェア