11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js 16 の重要な新機能をまとめて紹介します(第2回)

Posted at

Screenshot 2026-01-13 at 13.27.56.png

ご注意: 私の日本語はまだ十分ではないため、翻訳ツールを使用して記事を作成しています。表現が不自然な箇所があれば、ご容赦ください。:bow_tone1:

📑 目次


Enhanced Routing

Enhanced Routingとは?

Next.js 16は、ルーティングとナビゲーションシステムを完全に改善し、ページ遷移をより高速で軽量にします。

主な2つの改善点

1. Layout Deduplication (レイアウトの重複排除)

以前の問題:

  • 50個の商品リンクがあるページ → レイアウトを50回prefetch
  • 重複データを大量にダウンロード

Next.js 16の解決策:

  • 共通レイアウトは1回だけダウンロード
  • ネットワーク転送量を大幅に削減

実例:

// app/products/layout.tsx - このレイアウトは1回だけ読み込まれる
export default function ProductsLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div>
      <ProductHeader />
      <ProductSidebar />
      <main>{children}</main>
    </div>
  );
}

// app/products/page.tsx - 50個の商品リンクがあるページ
export default function ProductsPage() {
  return (
    <div>
      {products.map((product) => (
        <Link key={product.id} href={`/products/${product.id}`}>
          {product.name}
        </Link>
      ))}
    </div>
  );
}

結果:

  • 以前: レイアウトを50回読み込み(各リンクごとに1回)
  • 現在: レイアウトは1回だけ読み込み、残り49回はキャッシュから取得

2. Incremental Prefetching (段階的なPrefetch)

スマートな仕組み:

Next.js 16は、キャッシュにないものだけをprefetchします:

  • ✅ リンクがビューポートから外れたらリクエストをキャンセル
  • ✅ ホバー時やリンクがビューポートに戻ったら優先的にprefetch
  • ✅ データが無効化されたら再prefetch
  • ✅ Cache Componentsとシームレスに連携

例:

// ユーザーがスクロールダウン → リンクが表示 → Prefetch
// ユーザーがスクロールアップ → リンクが非表示 → Prefetchをキャンセル
// ユーザーが再びスクロールダウン → リンクが表示 → 再Prefetch

export default function ProductList() {
  return (
    <div>
      {/* このリンクは表示されている時だけprefetchされる */}
      <Link href='/products/1'>Product 1</Link>
      <Link href='/products/2'>Product 2</Link>
      <Link href='/products/3'>Product 3</Link>
      {/* ... 50 products */}
    </div>
  );
}

トレードオフ:

  • リクエスト数は増える
  • しかし転送総量は大幅に削減される

開発者へのメリット

コード変更不要 - すべて自動:

// コードはそのまま
<Link href="/dashboard">Dashboard</Link>
<Link href="/products/123">Product</Link>

// Next.js 16が自動的に最適化:
// - スマートなprefetch
// - 重複の排除
// - 不要なリクエストのキャンセル

Cache Componentsとの組み合わせ

// キャッシュされたレイアウト - 1回だけ読み込まれる
"use cache";

export default async function ProductLayout({ children }) {
  const categories = await getCategories(); // キャッシュ済み

  return (
    <div>
      <CategoryNav categories={categories} />
      {children}
    </div>
  );
}

// 動的ページ - 段階的なprefetch
export default async function ProductPage({ params }) {
  const product = await getProduct(params.id); // 最新データ
  return <ProductDetails product={product} />;
}

結果:

  • 超高速なナビゲーション
  • データは常に最新
  • 帯域幅の最適化

🔧 Improved Caching APIs

Improved Caching APIsとは?

Next.js 16は、キャッシュの動作をより明確に制御できるよう、キャッシュAPIを改善しました。

3つの主要API

1. revalidateTag() (更新)

Stale-While-Revalidate (SWR)を有効にするため、第2引数としてcacheLife profileが必要になりました:

import { revalidateTag } from "next/cache";

// ✅ 組み込みのcacheLife profileを使用(推奨: 'max')
revalidateTag("blog-posts", "max");

// または他のprofile
revalidateTag("news-feed", "hours");
revalidateTag("analytics", "days");

// またはカスタムインラインオブジェクト
revalidateTag("products", { expire: 3600 });

// ⚠️ 非推奨 - 1引数の形式
revalidateTag("blog-posts");

動作方法:

  • ユーザーはキャッシュされたデータを即座に受け取る
  • Next.jsはバックグラウンドで再検証
  • 静的コンテンツに適し、eventual consistencyを受け入れる

実例:

// タグ付きでデータを取得
const posts = await fetch("https://api.example.com/posts", {
  next: { tags: ["blog-posts"] },
});

// 新しい投稿がある場合、無効化
revalidateTag("blog-posts", "max");
// ユーザーは古いキャッシュを見る → バックグラウンドで更新 → 次回は新しいデータを見る

2. updateTag() (新規) - Read-Your-Writes

Server Actions専用のAPIで、read-your-writes semanticsを提供します:

"use server";

import { updateTag } from "next/cache";

export async function updateUserProfile(userId: string, profile: Profile) {
  await db.users.update(userId, profile);

  // キャッシュを無効化し、即座に更新
  // ユーザーは変更を即座に確認できる
  updateTag(`user-${userId}`);
}

使用タイミング:

  • フォーム、ユーザー設定
  • 変更を即座に確認する必要があるすべてのインタラクティブ機能
  • ユーザーが「更新を即座に確認できる」ことを期待する場合

revalidateTagとの比較:

API 動作 使用ケース
revalidateTag() SWR - 古いキャッシュ → バックグラウンド更新 静的コンテンツ、ブログ、商品
updateTag() Read-your-writes - 即座に更新 ユーザープロフィール、設定、フォーム

実例:

// ❌ 間違い: フォームにrevalidateTagを使用
export async function updateProfile(data: Profile) {
  await db.update(data);
  revalidateTag('profile', 'max'); // ユーザーは古いデータを見る!
}

// ✅ 正しい: updateTagを使用
export async function updateProfile(data: Profile) {
  await db.update(data);
  updateTag('profile'); // ユーザーは新しいデータを即座に確認できる!
}

3. refresh() (新規) - Uncached Data Only

Server Actions専用のAPIで、キャッシュされていないデータのみを更新します:

"use server";

import { refresh } from "next/cache";

export async function markNotificationAsRead(notificationId: string) {
  // データベースを更新
  await db.notifications.markAsRead(notificationId);

  // 通知カウントを更新(キャッシュされていないデータ)
  // キャッシュされたコンテンツには影響しない
  refresh();
}

使用タイミング:

  • 動的データの更新: 通知カウント、ライブメトリクス、ステータスインジケーター
  • キャッシュされたコンテンツ(ページシェル、静的コンテンツ)はそのまま維持
  • クライアント側のrouter.refresh()と補完的

実例:

// 通知カウント付きヘッダー(キャッシュなし)
export default async function Header() {
  const count = await getNotificationCount(); // キャッシュなし
  return <NotificationBadge count={count} />;
}

// メニュー付きサイドバー(キャッシュ済み)
("use cache");
export default async function Sidebar() {
  const menu = await getMenu(); // キャッシュ済み
  return <Nav items={menu} />;
}

// Server Action
export async function clearNotification() {
  await db.clear();
  refresh(); // カウントのみ更新、サイドバーには影響しない
}

Next.js 15からの移行

以前 (Next.js 15):

revalidateTag("blog-posts"); // 1引数

現在 (Next.js 16):

// SWRを使用した静的コンテンツ用
revalidateTag("blog-posts", "max");

// インタラクティブ機能用
updateTag("user-profile");

// キャッシュされていないデータ用
refresh();

ベストプラクティス

1. ユースケースに適したAPIを選択

// ✅ 静的コンテンツ → revalidateTag + SWR
revalidateTag("products", "max");

// ✅ ユーザーアクション → updateTag + read-your-writes
updateTag("user-cart");

// ✅ 動的インジケーター → refresh
refresh();

2. ほとんどのケースで'max' profileを使用

// ✅ 推奨: 長期間有効なコンテンツに'max'を使用
revalidateTag("blog-posts", "max");

// または必要に応じて他のprofile
revalidateTag("stock-prices", "hours"); // より頻繁に更新

3. タグの命名規則

// ✅ 良い: 説明的なタグ
revalidateTag("blog-posts", "max");
revalidateTag(`user-${userId}`, "max");
revalidateTag("product-list", "days");

// ❌ 悪い: 汎用的なタグ
revalidateTag("data", "max");
revalidateTag("cache", "max");

🎯 まとめ

Enhanced Routing

  • ✅ レイアウトとセグメントによる階層的なUI構成
  • ✅ 詳細なローディングとエラーバウンダリー
  • ✅ UX最適化のためのPPRとの組み合わせ
  • ✅ フルリロードなしのスムーズなナビゲーション

Improved Caching API

  • ✅ 暗黙的から明示的なキャッシュへの移行
  • ✅ 3つのレベル: Page, Component, Function
  • ✅ 管理しやすいタグベースの無効化
  • ✅ データ変更のためのServer Actionsとの組み合わせ

Happy Coding!
参考資料:

11
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?