➡️

Next.js 15 和訳

2024/10/22に公開

https://nextjs.org/blog/next-15

雑に翻訳しました。
意訳がめちゃくちゃ含まれているので注意です。


Next.js 15が正式に安定版としてリリースされました。
このリリースはRC1RC2のアップデートをベースにしています。
安定性に重点を置きながら、気に入っていただけるようなエキサイティングなアップデートを追加しました。
今すぐNext.js 15をお試しください。

# 新しい自動アップグレードCLIを使用する
npx @next/codemod@canary upgrade latest
 
# もしくは手動でアップグレードする
npm install next@latest react@rc react-dom@rc

また、今週木曜日(10月24日)のNext.js Confでは、次の新機能についてもご紹介します。
Next.js 15の新機能は以下の通りです。


Codemod CLIによるスムーズなアップグレード

Next.jsのメジャーリリースには必ずcodemod(自動化されたコード変換)が含まれており、変更点のアップグレードを自動化することができます。
アップグレードをさらにスムーズにするために、改良されたcodemod CLIをリリースしました。

npx @next/codemod@canary upgrade latest

このツールは、最新の安定版またはプレリリース版へのコードベースのアップグレードを手助けします。
CLIは依存関係を更新し、利用可能なcodemodを表示し、それらを適用する手順を案内します。

canaryタグはcodemodの最新バージョンを使用し、latestはNext.jsのバージョンを指定します。
Next.jsの最新バージョンにアップグレードする場合でも、canaryバージョンのcodemodを使うことをお勧めします。

Next.jsのcodemodsについてはこちらをご覧ください。


Async Request APIs (破壊的)

従来のサーバーサイド・レンダリングでは、サーバーはコンテンツをレンダリングする前にリクエストを待ちます。
しかし、すべてのコンポーネントがリクエスト固有のデータに依存しているわけではないので、それらをレンダリングするためにリクエストを待つ必要はありません。
理想的には、サーバーはリクエストが届く前にできるだけ多くの準備をしたいと考えています。
これを実現し、将来の最適化の下地を作るために、リクエストをいつ待つべきかを知る必要があります。

そのため、headercookieparamssearchParamsなどのリクエスト固有のデータに依存するAPIを非同期に移行します。

import { cookies } from 'next/headers';
 
export async function AdminPanel() {
  const cookieStore = await cookies();
  const token = cookieStore.get('token');
 
  // ...
}

これは互換性のない変更であり、以下のAPIに影響します:

  • cookies
  • headers
  • draftMode
  • layout.jspage.jsroute.jsdefault.jsgenerateMetadatagenerateViewport内のparams
  • page.js内のsearchParams

移行を容易にするために、これらのAPIには一時的に同期的にアクセスできますが、次のメジャーバージョンまで開発環境と本番環境で警告が表示されます。移行を自動化するためのcodemodが提供されています。

npx @next/codemod@canary next-async-request-api .

codemodが完全にコードを移行できない場合は、アップグレードガイドをお読みください。
また、Next.jsアプリケーションを新しいAPIに移行する方法の例も提供しています。


Caching Semantics

Next.jsのApp Routerは、デフォルトでキャッシュが有効となる状態でリリースされました。
これらの設定は、必要に応じてオプトアウトすることができるようにしながら、デフォルトで最もパフォーマンスの高いオプションを提供するように設計されています。

皆様からのフィードバックに基づいて、caching heuristicsを再評価し、Partial Prerendering(PPR)やfetchを使用するサードパーティのライブラリとどのように相互作用するかを検討しました。

Next.js 15では、fetchリクエスト、GETルートハンドラ、ClientRouterCacheのキャッシュのデフォルトを、キャッシュありからキャッシュなしに変更しました。
以前の動作を維持したい場合は、引き続きキャッシュをオプトインできます。

Next.jsのキャッシングについては、今後数ヶ月のうちに改善を続けていきます。

fetchリクエストはデフォルトではキャッシュされなくなりました。

Next.jsは、Web fetch APIのキャッシュオプションを使用して、サーバー側のフェッチリクエストがフレームワークの永続的なHTTPキャッシュとどのように相互作用するかを設定します。

fetch('https://...', { cache: 'force-cache' | 'no-store' });
  • no-store: リクエストごとにリモートサーバーからリソースをフェッチし、キャッシュを更新しない。
  • force-cache: キャッシュ(存在する場合)またはリモートサーバーからリソースをフェッチし、キャッシュを更新する。

Next.js 14では、cacheオプションが提供されていない場合、ダイナミック関数またはダイナミックコンフィグオプションが使用されていない限り、デフォルトでforce-cacheが使用されていました。

Next.js 15では、キャッシュオプションが提供されていない場合、デフォルトでno-storeが使用されます。
つまり、フェッチリクエストはデフォルトではキャッシュされません。

fetchリクエストのキャッシュを有効にするには、以下の方法があります。

  • fetch呼び出し時にcacheオプションforce-cacheに設定する。
  • Routeのdynamicオプションforce-staticに設定する。
  • RouteのfetchCacheオプションdefault-cacheに設定することで、レイアウトまたはページ内のすべてのfetchリクエストが、明示的に独自のキャッシュオプションを指定しない限り、force-cacheを使用するように上書きする。

GETルートハンドラがデフォルトでキャッシュされなくなりました。

Next 14では、GETHTTPメソッドを使用するルートハンドラは、動的関数または動的設定オプションを使用しない限り、デフォルトでキャッシュされていました。
Next.js 15では、GET関数はデフォルトではキャッシュされません。

export dynamic = 'force-static'のような静的なルート設定オプションを使って、キャッシュを有効にすることもできます。

sitemap.tsopengraph-image.tsxicon.tsxなどの特殊なルートハンドラや、その他のメタデータファイルは、動的関数や動的設定オプションを使用しない限り、デフォルトでは静的なままです。

ClientRouterCacheは、デフォルトでページ・コンポーネントをキャッシュしなくなりました。

Next.js 14.2.0では、ルーターキャッシュのカスタム設定を可能にする実験的なstaleTimesフラグを導入しました。

Next.js 15では、このフラグはまだアクセス可能ですが、デフォルトの動作が変更され、ページセグメントのstaleTime0になります。
これは、アプリをナビゲートするときに、ナビゲーションの一部としてアクティブになったページコンポーネントの最新データが常にクライアントに反映されることを意味します。
しかし、変わらない重要な動作もあります。

  • PartialRenderingをサポートし続けるために、共有レイアウトデータはサーバーからリフェッチされません。
  • 戻る/進むナビゲーションは、ブラウザがスクロール位置を復元できるようにキャッシュから復元されます。
  • loading.jsは5分間(またはstaleTimes.static設定の値)キャッシュされます。

以下の設定を行うことで、以前のClientRouterCacheの動作に切り替えることができます。

const nextConfig = {
  experimental: {
    staleTimes: {
      dynamic: 30,
    },
  },
};
 
export default nextConfig;

React 19

Next.js 15のリリースの一環として、React 19の次期リリースを使用することを決定しました。
Next.js 15では、App RouterではReact 19 RCを使用しており、またコミュニティのフィードバックに基づいてPages RouterにReact 18の後方互換性も導入しました。
Pages Routerを使用している場合は、準備ができ次第React 19にアップグレードできます。

React 19はまだRCの段階ですが、実世界のアプリケーションでの広範なテストとReactチームとの緊密な連携により、その安定性には自信があります。
コアの変更点は十分にテストされており、既存のApp Routerユーザーに影響を与えることはありません。
そのため、Next.js 15を安定版としてリリースすることにしました。

可能な限りスムーズに移行できるよう、私たちは移行プロセスを容易にするためのcodemodと自動化ツールを提供しています。

詳しくは、Next.js 15アップグレードガイドReact 19アップグレードガイドReact Conf Keynoteをご覧ください。

Pages Router on React 18

Next.js 15は、React 18とのPages Routerの後方互換性を維持しており、ユーザーはNext.js 15の改良点の恩恵を受けながらReact 18を使い続けることができます。

最初のリリース候補版(RC1)以降、コミュニティからのフィードバックに基づき、React 18のサポートに重点を移してきました。
この柔軟性により、React 18でPages Routerを使用しながらNext.js 15を採用することが可能になり、アップグレードパスをより自由にコントロールできるようになりました。

React Compiler (実験的)

React Compilerは、MetaのReactチームによって作られた新しい実験的なコンパイラです。
このコンパイラは、プレーンなJavaScriptのセマンティクスとReactのルールを理解することで、あなたのコードを深いレベルで理解し、コードに自動最適化を加えることを可能にします。
コンパイラは、useMemouseCallbackなどのAPIを通じて、開発者が手作業で行うメモ化の量を減らし、コードをよりシンプルに、メンテナンスしやすく、エラーの発生しにくいものにします。

Next.js 15では、React Compilerのサポートが追加されました。 React CompilerとNext.jsの設定オプションについてはこちらをご覧ください。

ハイドレーションエラーの改善

Next.js 14.1では、エラーメッセージとハイドレーションエラーが改善されました。
Next.js 15では、改善されたハイドレーションエラーの表示を追加することで、これらの改善を続けています。
ハイドレーションエラーは、問題の対処方法の提案とともに、エラーのソースコードを表示するようになりました。

たとえば、これはNext.js 14.1で表示されていたハイドレーションのエラーメッセージです。

Next.js15ではこのように改善されます。


Turbopack Dev

私たちは next dev --turboが安定版となり、開発体験を加速させるために使用できるようになったことを喜んでお知らせします。
私たちはすでに vercel.comnextjs.orgv0、および他のすべてのアプリケーションでこれを使用して反復を重ね、素晴らしい結果を得ています。

例えば、大規模なNext.jsアプリであるvercel.comでは、次のようなことがありました。

  • ローカルサーバーの起動が最大76.7%高速化。
  • Fast Refreshにより、コード更新が最大96.3%高速化。
  • キャッシュを使用しない場合、ルートの初期コンパイルが最大45.8%高速化されます(Turbopackにはまだディスクキャッシング機能がありません)。

Turbopack Devについての詳細は、新しいブログ記事をご覧ください。


静的ルートインジケーター

Next.jsは、開発中にスタティックルートインジケータを表示するようになりました。
この視覚的な手がかりにより、ページがどのようにレンダリングされるかを理解し、パフォーマンスを最適化しやすくなります。

また、next buildを使用して、すべてのルートのレンダリング戦略を表示することもできます。

このアップデートは、Next.jsのオブザーバビリティを強化し、開発者がアプリケーションを監視、デバッグ、最適化しやすくするための継続的な取り組みの一環です。
専用の開発者ツールも開発中で、詳細は近日中に公開します。

スタティックルートインジケータ(無効化可能)の詳細については、こちらをご覧ください。


unstable_afterによるレスポンス後のコード実行(実験的)

ユーザーリクエストを処理するとき、サーバーは通常、レスポンスの計算に直接関連するタスクを実行します。
しかし、ロギング、分析、その他の外部システム同期などのタスクを実行する必要があるかもしれません。

これらのタスクはレスポンスとは直接関係ないので、ユーザーは完了するまで待つ必要はないはずです。
サーバーレスの関数は、レスポンスが終了すると直ちに計算を停止するため、ユーザーへのレスポンス後の作業を延期することが課題となります。

after()は、この問題を解決する新しい実験的なAPIで、レスポンスのストリーミングが終了した後に処理する作業をスケジューリングできるようにし、プライマリ・レスポンスをブロックすることなくセカンダリ・タスクを実行できるようにします。

使用するには、next.config.jsexperimental.afterを追加します。

const nextConfig = {
  experimental: {
    after: true,
  },
};
 
export default nextConfig;

次に、Server Components、Server Actions、Route Handlers、または Middleware で関数をインポートします。

import { unstable_after as after } from 'next/server';
import { log } from '@/app/utils';
 
export default function Layout({ children }) {
  // Secondary task
  after(() => {
    log();
  });
 
  // Primary task
  return <>{children}</>;
}

unstable_afterの詳細についてはこちらをご覧ください。


instrumentation.js (安定版)

instrumentationファイルとregister()APIにより、ユーザーはNext.jsサーバーのライフサイクルを利用して、パフォーマンスを監視し、エラーの原因を追跡し、OpenTelemetryのような観測可能なライブラリと深く統合することができます。

この機能は現在安定しており、experimental.instrumentationHook設定オプションを削除することができます。

さらに、Sentryと協力して、新しいonRequestErrorフックを設計しました。

  • サーバーで発生したすべてのエラーについて、以下のような重要なコンテキストを取得する。
    • Route: PageRouterまたはAppRouter
    • Server context: Server Component、Server Action、Route Handler、Middleware
  • エラーをお気に入りのオブザーバビリティプロバイダーに報告する。
export async function onRequestError(err, request, context) {
  await fetch('https://...', {
    method: 'POST',
    body: JSON.stringify({ message: err.message, request, context }),
    headers: { 'Content-Type': 'application/json' },
  });
}
 
export async function register() {
  // お好きなオブザーバビリティプロバイダーのSDKをインストールしてください。
}

onRequestError関数の詳細については、こちらをご覧ください。


<Form>コンポーネント

新しい <Form>コンポーネントはHTMLの<form>要素を拡張し、プリフェッチクライアントサイドナビゲーション、プログレッシブエンハンスメントを備えたコンポーネントです。
検索フォームから結果ページに誘導するような、新しいページにナビゲートするフォームに便利です。

import Form from 'next/form';
 
export default function Page() {
  return (
    <Form action="/search">
      <input name="query" />
      <button type="submit">Submit</button>
    </Form>
  );
}

<Form>コンポーネントには以下の機能が含まれます。

  • プリフェッチ: フォームが表示されているとき、レイアウトとローディング UI がプリフェッチされ、ナビゲーションが高速になります。
  • クライアントサイドナビゲーション: フォームの送信時に、共有レイアウトとクライアント側の状態が保持されます。
  • プログレッシブエンハンスメント: JavaScript がまだ読み込まれていない場合でも、フォームはフルページナビゲーションで動作します。

以前は、これらの機能を実現するためには多くの手作業による定型文が必要でした。 例えば

サンプル
// Note: これはデモンストレーションのため省略されている部分があります。
// 本番コードでの使用は推奨しません。

'use client'
 
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
 
export default function Form(props) {
  const action = props.action
  const router = useRouter()
 
  useEffect(() => {
    // フォームのターゲットがURLの場合、プリフェッチする
    if (typeof action === 'string') {
      router.prefetch(action)
    }
  }, [action, router])

  function onSubmit(event) {
    event.preventDefault()
 
    // すべてのフォームフィールドを取得し、データのURLをエンコードして `router.push` をトリガーする。
    const formData = new FormData(event.currentTarget)
    const data = new URLSearchParams()
 
    for (const [name, value] of formData) {
      data.append(name, value as string)
    }
 
    router.push(`${action}?${data.toString()}`)
  }
 
  if (typeof action === 'string') {
    return <form onSubmit={onSubmit} {...props} />
  }
 
  return <form {...props} />
}

<Form>コンポーネントの詳細については、こちらをご覧ください。


next.config.tsのサポート

Next.jsがTypeScriptのnext.config.tsファイルをサポートし、オートコンプリートと型安全にするためのNextConfig型を提供するようになりました。

import type { NextConfig } from 'next';
 
const nextConfig: NextConfig = {
  /* config options here */
};
 
export default nextConfig;

Next.jsのTypeScriptサポートについてはこちらをご覧ください。


セルフホスティングの改善

アプリケーションをセルフホスティングする際、Cache-Controlディレクティブに対するより詳細な制御が必要になることがあります。
一般的なケースとしては、ISRページに対して送信されるstale-while-revalidate期間の制御があります。そこで、以下の2点を改善しました。

  1. next.configexpireTimeを設定できるようになりました。これは以前はexperimental.swrDeltaオプションとして知られていました。
  2. デフォルト値を1年に更新し、ほとんどのCDNが意図されたstale-while-revalidateセマンティクスを完全に適用できるようにしました。

また、カスタムのCache-Control値がデフォルト値で上書きされることがなくなり、完全な制御が可能になり、どのCDN設定とも互換性を確保できるようになりました。

最後に、セルフホスティング時の画像最適化を改善しました。これまでは、Next.jsサーバーで画像を最適化するためにsharpのインストールを推奨していましたが、この推奨が時々見逃されることがありました。
Next.js 15では、sharpを手動でインストールする必要がなくなり、next startスタンドアロン出力モードで実行する際にNext.jsが自動的にsharpを使用します。

詳細については、セルフホスティングNext.jsに関する新しいデモとチュートリアルビデオをご覧ください。


Server Actionsの強化されたセキュリティ

Servert Actionsとは、クライアント側から呼び出すことができるサーバー側の関数です。
これらはファイルの先頭にuse serverディレクティブを追加し、非同期関数をエクスポートすることで定義されます。

Servert Actionsやユーティリティ関数がコード内の他の場所でインポートされていない場合でも、一般にアクセス可能なHTTPエンドポイントであることに変わりはありません。
この動作は技術的には正しいものの、意図せずそのような関数が公開されてしまう可能性があります。

セキュリティを向上させるために、以下の強化策を導入しました。

  • デッドコードの排除: 使用されていないServert ActionsのIDはクライアント側のJavaScriptバンドルに公開されなくなり、これによりバンドルサイズが小さくなり、パフォーマンスが向上します。
  • 安全なアクションID: Next.jsは、クライアントがServert Actionsを参照および呼び出すことを可能にするために、推測不可能かつ非決定的なIDを作成します。これらのIDは、セキュリティを強化するためにビルド間で定期的に再計算されます。
// app/actions.js
'use server';

// このアクションはアプリケーションで使用されていますので、
// Next.jsはクライアントがサーバーアクションを参照して呼び出せるように安全なIDを作成します。
export async function updateUserAction(formData) {}

// このアクションはアプリケーションで使用されていないため、
// Next.jsは`next build`時にこのコードを自動的に削除し、公開エンドポイントを作成しません。
export async function deleteUserAction(formData) {}

サーバーアクションはパブリックなHTTPエンドポイントとして扱う必要があります。
サーバーアクションのセキュリティについて詳しくは、こちらをご覧ください。


外部パッケージのバンドルの最適化 (安定版)

外部パッケージをバンドルすると、アプリケーションのコールドスタートのパフォーマンスが向上します。
App Routerでは、外部パッケージはデフォルトでバンドルされますが、新しいserverExternalPackagesオプションを使用して特定のパッケージをオプトアウトできます。

Pages Routerでは、外部パッケージはデフォルトではバンドルされませんが、既存のtranspilePackagesオプションを使用して、バンドルするパッケージのリストを提供することができます。
この設定オプションでは、各パッケージを指定する必要があります。

App RouterとPages Routerの設定を統一するために、App Routerのデフォルトの自動バンドルに合うように、新しいオプションbundlePagesRouterDependenciesを導入します。
必要に応じて、serverExternalPackagesを使って特定のパッケージをオプトアウトできます。

const nextConfig = {
  // Pages Routerで外部パッケージを自動的にバンドルする
  bundlePagesRouterDependencies: true,
  // App Router、Pages Router両方のバンドルから特定のパッケージをオプトアウトする
  serverExternalPackages: ['package-name'],
};
 
export default nextConfig;

外部パッケージの最適化についてはこちらをご覧ください。


ESLint 9 サポート

Next.js 15では、2024年10月5日にESLint 8のサポートが終了するのに伴い、ESLint 9もサポートします。

スムーズな移行を保証するため、Next.jsはESLint 8と9の後方互換性を維持しています。

ESLint 9にアップグレードして、まだ新しい設定形式を採用していないことが判明した場合、Next.jsは自動的にESLINT_USE_FLAT_CONFIG=falseエスケープハッチを適用し、移行を容易にします。

さらに、-ext-ignore-pathのような非推奨オプションはnext lint実行時に削除されます。
ESLintは最終的にESLint 10でこれらの古い設定を無効するため、すぐに移行を開始することをお勧めします。

これらの変更の詳細については、移行ガイドをご覧ください。

今回のアップデートの一環として、eslint-plugin-react-hooksv5.0.0にアップグレードし、React Hooksの使用に関する新しいルールを導入しました。 すべての変更点はeslint-plugin-react-hooks@5.0.0の変更履歴で確認できます。


開発とビルドの改善

Server Components HMR

開発中には、保存時にサーバーコンポーネントが再実行されます。
これは、APIエンドポイントやサードパーティのサービスへのフェッチリクエストも呼び出されることを意味します。

ローカル開発のパフォーマンスを向上させ、課金されるAPIコールの潜在的なコストを削減するために、ホットモジュールリプレースメント(HMR)が以前のレンダーからのフェッチレスポンスを再利用できるようになりました。

サーバーコンポーネントのHMRキャッシュの詳細については、こちらをご覧ください。

AppRouterの静的生成の高速化

ビルド時間を改善するために静的生成を最適化しました。
特に、ネットワークのリクエストが遅いページにおいて効果的です。

以前は、静的最適化プロセスでページを2回レンダリングしていました。
1回目はクライアントサイドのナビゲーション用のデータを生成するためで、2回目は最初のページ訪問時のHTMLをレンダリングするためでした。今では、1回目のレンダリングを再利用することで2回目のプロセスを省略し、作業量とビルド時間を削減しています。

さらに、静的生成ワーカーは、今やページ間でフェッチキャッシュを共有しています。
フェッチコールでキャッシュをオプトアウトしない限り、その結果は同じワーカーが処理する他のページでも再利用されます。
これにより、同じデータに対するリクエストの数が削減されます。

静的生成の拡張(実験的)

より高度なユースケースのために、静的生成プロセスに対する制御を強化する実験的なサポートを追加しました。
これらのオプションはリソースの使用量の増加や、並行処理の増加によってメモリ不足のエラーが発生する可能性があるため、特定の要件がない限り現在のデフォルト設定を使用することをお勧めします。

const nextConfig = {
  experimental: {
	// ビルドに失敗したページ生成を何回リトライするか。
    staticGenerationRetryCount: 1
    // Worker1つあたり何ページ並列で処理するか
    staticGenerationMaxConcurrency: 8
    // 新しいWorkerを起動する為に必要な最小ページ数
    staticGenerationMinPagesPerWorker: 25
  },
}
 
export default nextConfig;

静的生成のオプションについてはこちらをご覧ください。


PrAha

Discussion