Next.js 15.1を読み解いていく

Next.js 15.1を読み解いていく

こんにちは!TechJourneyを運営しているkoimaiです!

今回は、2024年12月10日にVercelから公開された「Next.js 15.1」を読み解いていきたいと思います。

私はNext.jsを使用することが多いので、最新の情報をキャッチアップして、それを記事としてまとめていくのが主目的です。

本記事は、Claude 3.5 Sonnetを使用しながら、書かれております。

はじめに

Next.js 15の新しいバージョン15.1がリリースされました。

新しい情報が出たというよりは、以前から導入すると言われていたことが、安定版として使えるようになった感じです。

Next.js 15.1のリリース概要

2024年12月10日、VercelはNext.js 15.1をリリースしたのと同時に内容を説明した記事を公開しました。

Next.js 15.1における主要な更新内容は以下の4つに集約されます。

  • React 19の安定版サポート
    • Page RouterとApp Router両方でのサポート
    • 最新のReact機能へのアクセス
  • Error Debuggingの強化
    • より直感的なエラー表示
    • Source Mapsの改善
    • Stack Traceの最適化
  • after APIの安定版リリース
    • レスポンス完了後の処理を効率的に実行
    • ログ記録や分析などのユースケースに対応
  • 新しい認証関連APIs(実験段階)
    • forbidden()とunauthorized()の導入
    • より細やかな認証エラーハンドリング

以降のセクションでは、これらの更新内容について詳しく見ていきたいと思います。

React 19のサポート(stable)

Next.js 15.1の最も重要な更新の一つが、React 19の安定版サポートです。

これまでReact 19を使用するには、リリース候補(RC)版やCanaryビルドが必要でしたが、今回のアップデートにより正式にサポートされることになりました。

Pages RouterとApp Routerでの対応の違い

Next.jsの二つの主要なルーティングシステムでは、React 19の扱いが若干異なっている:

  • Pages Router
    • React 19が完全に安定版としてサポート
    • React 18との広報互換性も維持
    • 従来のアプリケーションからの段階的な意向が可能
  • App Router
    • React Canaryリリースが引き続き組み込まれる
    • 安定版のReact 19の変更に加え、新機能のプレビューも利用可能
    • フレームワークレベルでの検証済み機能を優先的に利用可能

sibling pre-warmingについて

Next.js 15のリリース以降、React 19の重要な追加機能として「sibling pre-warming」が導入されたことが記事で紹介されています。

この機能は、ユーザーインタラクションを予測し、関連するコンポーネントを事前に準備することで、アプリケーションのパフォーマンスを向上させます。

主な特徴:

  • ユーザーの行動パターンに基づく最適化
  • 関連コンポーネントの事前ロード
  • ナビゲーション体験の向上

Next.jsはReactがベースとなっているので、React 19がサポートされたことによって、本番環境で使用することも考えられます。

ただ、プロジェクトで使用するサードパーティライブラリのReact 19の対応状況を確認したりする必要はありますね。

Error Debuggingの改善

Next.js 15.1では、エラーデバッグの機能が改善されました。

特にWebpackとTurbopackの両方で、エラーの原因特定がより簡単になっています。

また、ブラウザ、ターミナル、デバッガーのどこでエラーが発生しても、よりわかりやすく表示されるようになりました。

Source Mapsの強化

そもそもSource Mapsって何なんでしょうか?

Source Mapsは、ビルド・圧縮・最適化されたコードと、元のソースコードを紐づける仕組みのこと。

例えば、本番環境用に圧縮されたJavaScriptファイルでエラーが発生した場合、Source Mapsがあれば、開発者は元のソースコードのどの行で問題が起きたのかを特定できます。

Next.js 15.1では、このSource MapsにignoreListというプロパティが実装されました。

これにより、外部依存関係のスタックフレームを非表示にして、自分たちのアプリケーションコードに焦点を当てやすくなっています。

また、メソッド名のソースマッピングの精度が向上したTurbopackの使用がお勧めされています。

TurbopackはNext.js 15で安定版となったので、これを機に使ってみるのも良いかもしれませんね。

Stack Framesの最適化

Stack Framesというのは、プログラムの実行履歴を示すものです。

関数やメソッドが呼び出されるたびに、その情報がスタックに積み重ねられていきます。

エラーが発生した時、このStack Framesを見ることで、どの関数からどの関数が呼ばれて、最終的にエラーが発生したのかがわかる仕組みです。

Next.js 15.1では、このStack Framesの表示ロジックが改善されました:

  • サードパーティの依存関係のフレームがデフォルトで非表示に
  • 必要な時だけ「Show ignored frames」をクリックして全体を確認可能
  • ブラウザとターミナルで一貫した表示形式に

開発者体験の向上ポイント

エラーデバッグの改善により、開発者の日常的な作業がかなり楽になりそうです:

  1. エラーの場所が一目でわかる
    • 自分のコードに関係する部分が優先表示
    • 余計な情報が整理されて見やすい
  2. プロファイリングの改善
    • パフォーマンスの問題箇所を特定しやすく
    • 外部ライブラリのノイズを除外可能に
  3. Edge Runtimeでの改善
    • 開発環境でもエラースタックが一貫して表示
    • エラーメッセージだけでなくスタックも確認可能に

Before / Afterの具体例

実際の改善点を画像で見てみましょう。

Before(改善前):

Error DebuggingのBeforeの具体例
https://nextjs.org/blog/next-15-1
  • エラーコンポーネントが大きい
  • Call Stackというセクションで外部依存関係のスタックフレームも全て表示
  • Show collapsed framesというオプション

After(改善後):

Error DebuggingのAfterの具体例
https://nextjs.org/blog/next-15-1
  • エラーコンポーネントがよりコンパクトに
  • Pageというシンプルなセクションになり、関連性の高い情報のみが表示
  • Show ignored framesという、より具体的で理解しやすいオプション

この例はシンプルなエラーですが、複数のライブラリを使用していたり、多くのファイルが影響しているエラーが発生した際に、デバッグ作業の効率が上がりそうですね。

VercelのDelbaさんがX(Twitter)に投稿していた内容も、一目で違いがわかるものでした。

after API(stable)

Next.js 15.1で、after APIが安定版としてリリースされました。

このAPIは、レスポンスが完了した後に処理を実行できる機能です。これまでNext.js 15のリリース候補版でunstable_afterとして提供されていた機能が、安定版として利用できるようになりましたね。

APIの概要と使用例

after APIは、レスポンス(またはプレレンダリング)が完了した後に実行される処理をスケジュールできます。

使用可能な場所は以下の4つです:

  • Server Components(generatedMetadata含む)
  • Server Actions
  • Route Handlers
  • Middleware

基本的な使用例を見てみましょう:

import { after } from 'next/server'
import { log } from '@/app/utils'

export default function Layout({ children }) {
  after(() => {
    // レイアウトのレンダリングが完了してユーザーに送信された後に実行
    log()
  })
  return <>{children}</>
}

ユースケース(ログ記録、分析等)

主なユースケースとしては以下のようなものが考えられます:

  • アナリティクス処理
    • ユーザーの行動ログの記録
    • パフォーマンスメトリクスの収集
  • システム同期
    • 外部システムへの通知
    • キャッシュの更新

具体的な例を見てみましょう:

import { after } from 'next/server'
import { cookies, headers } from 'next/headers'

export async function POST(request: Request) {
  // メインの処理
  const result = await processData()

  // ユーザーアクティビティのログを記録
  after(async () => {
    const userAgent = await headers().get('user-agent') || 'unknown'
    const sessionId = await cookies().get('session-id')?.value || 'anonymous'

    // ログ記録(メインの処理をブロックしない)
    await logUserActivity({ sessionId, userAgent })
  })

  return new Response(JSON.stringify(result))
}

実装方法と注意点

after APIを使用する際の重要なポイントをまとめてみましょう:

  1. 静的ページでの動作
    • 静的ページで使用した場合、ビルド時に実行
    • ページが再検証されるときにも実行
  2. エラーハンドリング
    • エラーが発生してもafterの処理は実行される
    • notFoundredirectが呼ばれた場合も同様
  3. 制限事項
    • Server ComponentsではcookiesheadersなどのリクエストAPIは使用不可
    • Partial Prerenderingとの関係で制限がある
  4. 実行時間
    • プラットフォームのデフォルト設定またはmaxDurationの制限に従う
    • 長時間の処理は注意が必要

また、after APIは複数回ネストして使用することもできます。

ユーティリティ関数として機能を追加するラッパーを作成するような場合に便利ですね。

import { after } from 'next/server'

export function trackPageView(pageData: PageData) {
  after(async () => {
    await sendAnalytics(pageData)
  })
}

export function trackUserAction(actionData: ActionData) {
  after(async () => {
    await logUserBehavior(actionData)
  })
}

// Component内での使用例
export default function Page() {
  // 複数のafter処理を組み合わせて使用
  trackPageView({ page: 'home' })
  trackUserAction({ action: 'view' })
  
  return <div>Page content</div>
}

React cacheを使用して、after内で呼び出される関数を重複排除することもできます。

これは、同じような処理が複数回実行されることを防ぐのに役立ちそうです。

import { cache } from 'react'
import { after } from 'next/server'

// キャッシュされる関数
const cachedAnalytics = cache(async (userId: string) => {
  const result = await fetchUserAnalytics(userId)
  return result
})

export default function Page() {
  after(async () => {
    // 同じuserIdに対する呼び出しは1回だけ実行される
    const analytics1 = await cachedAnalytics('user123')
    const analytics2 = await cachedAnalytics('user123') // キャッシュから返される
  })

  return <div>Page content</div>
}

これらの特徴を活かすことで、メインの処理をブロックすることなく、補助的なタスクを効率的に実行できそうですね。

認証関連の新しいAPIs(experimental)

Next.js 15.1では、認証周りのエラーハンドリングを改善する新しいAPIが実験的な機能として追加されました。

App Routerで使用できるnotFount()に似た機能として、forbidden()unauthorized()が導入されています。

まだ実験的な機能なので、本番環境での使用はまだ避けた方がいいですね。

forbidden()とunauthorized()の紹介

これらのAPIを使用するには、まずnext.config.tsで実験的機能を有効にする必要があります:

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    authInterrupts: true,
  },
}

export default nextConfig

2つのAPIの使い分けについて説明しましょう:

  • forbidden()
    • HTTPステータスコード:403
    • ユーザーは認証されているが、リソースへのアクセス権限がない場合に使用
    • カスタマイズはforbidden.tsxで実装
  • unauthorized()
    • HTTPステータスコード:401
    • ユーザーが認証されていない場合に使用
    • カスタマイズはunauthorized.tsxで実装

カスタムエラーページの実装方法

それぞれのエラーに対して、カスタムページを作成できます。いかが基本的な実装例です:

mport Link from 'next/link'

export default function Forbidden() {
  return (
    <div className="p-8 text-center">
      <h2 className="text-2xl font-bold mb-4">アクセス権限がありません</h2>
      <p className="mb-4">このリソースにアクセスする権限がないようです。</p>
      <Link href="/" className="text-blue-500 hover:underline">
        ホームに戻る
      </Link>
    </div>
  )
}
import Link from 'next/link'

export default function Unauthorized() {
  return (
    <div className="p-8 text-center">
      <h2 className="text-2xl font-bold mb-4">ログインが必要です</h2>
      <p className="mb-4">このページを表示するにはログインが必要です。</p>
      <Link href="/login" className="text-blue-500 hover:underline">
        ログインページへ
      </Link>
    </div>
  )
}

実践的な使用例

実際のアプリケーションでどのように使用するか、具体例を見てみましょう:

import { forbidden } from 'next/navigation'
import { verifySession } from '@/lib/auth'

export default async function AdminPage() {
  const session = await verifySession()

  // 管理者権限のチェック
  if (session.role !== 'admin') {
    forbidden()
  }

  return <div>管理者ページの内容</div>
}
import { forbidden, unauthorized } from 'next/navigation'
import { getSession, getUserData } from '@/lib/auth'

export default async function UserDashboard({ params }: { params: { id: string } }) {
  const session = await getSession()

  // 認証チェック
  if (!session) {
    unauthorized()
  }

  const userData = await getUserData(params.id)

  // 自分のデータ以外へのアクセスをブロック
  if (session.userId !== params.id) {
    forbidden()
  }

  return <div>ダッシュボードの内容</div>
}

この機能は、認証・認可のエラーハンドリングをよりクリーンに実装できそうですね。

また、この機能はClerkというサービスからの提案で実装されたようです。認証サービスと連携する際の使い勝手が良さそうですね。

まだ実験的な機能なので、安定版リリースを待っておきたいと思います。

しかし、Next.js 15.2では安定版のリリースをする予定はなく、他のAPIも拡張して実装していくことを優先すると公式記事で述べられています。

アップグレード方法

Next.js15.1へのアップグレードは、自動と手動の2つの方法が用意されています。

アップデート内容には関係ありませんが、本記事でもまとめておきます。

自動アップグレード方法

Next.jsは公式でアップグレード用のCLIツールを提供しています。

以下のコマンドで自動アップグレードを実行できます:

npx @next/codemod@canary upgrade latest

このコマンドは以下のような処理を自動で行ってくれます:

  • 依存関係の更新
  • 非推奨APIの更新
  • 設定ファイルの必要な変更

ただし、自動アップグレードをする前には、プロジェクトの変更をコミットしておくのが安心ですね。

手動アップグレード方法

手動でアップグレードする場合は、package.jsonを直接編集します。

以下のコマンドで必要なパッケージを最新版にアップデートします。

npm install next@latest react@latest react-dom@latest

もし新規プロジェクトを始める場合は、以下のコマンドで最新版のNext.jsを使用できます:

npx create-next-app@latest

手動アップグレードの場合は、自動アップグレードに対して、サードパーティライブラリの互換性を確認する必要性があります。

まとめ

React 19の安定版サポートが実装され、Pages RouterとApp Router両方で利用可能になりました。

デバッグ体験も改善され、ソースマップの強化やスタックフレームの効率的な表示により、エラーの特定が容易になりました。

新APIとして、レスポンス完了後の処理を実行できるafterが安定版として、認証エラー処理を細かく制御できるforbiddenunauthorizedが実験的機能として追加されました。

今後のアップデートでは、エラーオーバーレイUIの刷新が予定されており、15.2では認証関連APIのさらなる機能拡張が計画されています。

特に認証周りの改善は、より広範なユースケースをカバーできるように進化していく見込みですね。

最後まで読んでいただき、ありがとうございました!