こんにちは!TechJourneyを運営しているkoimaiです!
Next.js公式XからNext.js 15のリリース候補(RC2)についてのツイートが2024/10/16の4:46amにされました。
改めて、日本語の記事を書いて、情報のキャッチアップをしやすくなればと思います。
このRCのバージョンは、2024/10/16現在、正規にリリースされたわけではなく、早期バージョンとして今後の安定版のリリース前に最新の機能を触れることができるバージョンです。
Next.js 15のリリース情報(RC)についても、記事にしているので、良かったら参考にしてみてください。
Next.js 15 RC2の要約
2024年5月に最初のNext.js 15リリース候補版を発表してからのフィードバックをもとに、2回目のリリース候補版を準備してきた内容になっています。
- @next/codemodのアップグレード
- 最新のNext.jsとReactのバージョンに簡単にアップグレードすることができるようになった
- Turbopackの改善
- パフォーマンス改善
- Next.js 15の安定性向上
- Async Request APIs(破壊的な変更)
- シンプルなレンダリング
- キャッシュの仕組み
- Server Actionsのセキュリティ向上
- 推測不可能なエンドポイント
- 未使用のアクションの削除
- Static Route Indicator
- 開発環境で静的なルートを表す新しい視覚的な指標
- <Form>コンポーネント
- クライアント側のナビゲーションでHTMLフォームを強化
- next.config.ts
- Next.jsの設定ファイルであるnext.configのTypeScriptサポート
- instrumentation.js(安定版)
- サーバーのライフサイクルを観測するための新しいAPI
- 開発とビルドの改善
- ビルド時間の改善
- 高速リフレッシュの高速化
- セルフホスティング機能の改善
- Cache-Controlヘッダーの制御を強化
- ESLint 9 サポート
- ESLint 9のサポートを追加
2024/10/17現在、すでにNext.js 15リリース候補版(RC2)を試すことができます。コマンドの内容は次に説明しています。
# 新しく用意された自動アップグレードCLIを使用する
npx @next/codemod@canary upgrade rc
# 手動でアップグレードをする
npm install next@rc react@rc react-dom@rc
codemod CLIによるスムーズなアップグレード
Next.jsのメジャーリリースには必ず、codemodsが含まれています。codemodsとは、バージョンアップによる変更点のアップグレードを自動で行ってくれるCLIです。
そして今回も、Next.js 15のために新しくcodemod CLIがリリースされました。
使用方法は以下のコマンドを実行することです。
npx @next/codemod@canary upgrade rc
codemod CLIの主な機能
codemod CLIの主な機能は、以下のようなものがあります。
- 依存関係の自動更新:プロジェクトの依存関係を最新バージョンに自動で更新してくれます。
- 利用可能なcodemodの表示:適用可能な自動コード変換の一覧を表示してくれます。
- ガイド付きアップグレード:codemodの適用プロセスをステップバイステップで進めてくれます。
- 柔軟なバージョン指定:コマンドライン上で指定したディストタグ(@rc, @canaryなど)に基づいて、アップグレード先のバージョンを決定できます。
これらによって開発者としては、時間を節約できたり、自動化によるヒューマンエラーのリスクを減少したり、そういったメリットがあると思います。
実際にcodemodでバージョンアップしてみた
Next.jsの新規プロジェクトをv14.2.15で作成してから、実際にcodemodを使用してバージョンアップをしてみました!
以下はcodemodを使用して、ステップバイステップでアップグレードを進めている序盤の一部になります。
最初に”next dev”で、Turbopackを有効にするかどうかを聞かれました。
その次に、アップデート内容の以下の4つの項目を適用するかどうか選択できました。
- (v15.0.0-canary.179) app-dir-runtime-config-experimental-edge – Transform App Router Route Segment Config
runtime
value fromexperimental-edge
toedge
- App Router(app directory)のルートセグメント設定において、
runtime
の値をexperimental-edge
からedge
に変換します。
- App Router(app directory)のルートセグメント設定において、
- (v15.0.0-canary.171) next-async-request-api – Transforms usage of Next.js async Request APIs
- Next.jsの非同期リクエストAPIの使用方法を新しい形式に変換します。
- (v15.0.0-canary.153) next-request-geo-ip – Install
@vercel/functions
to replacegeo
andip
properties onNextRequest
NextRequest
のgeo
とip
プロパティを置き換えるために、@vercel/functions
パッケージをインストールします。
- (v15.0.0-canary.44) next-dynamic-access-named-export – Transform
next/dynamic
imports accessing named exports to return an object with adefault
propertynext/dynamic
のインポートにおいて、名前付きエクスポートへのアクセスをdefault
プロパティを持つオブジェクトを返すように変換します。
このように、ガイドに従いながら、アップグレードすることができました!
開発環境用のTurbopackの安定版リリース
Next.js 15の正式リリースにおいて、開発環境用のTurbopackが安定版としてリリースされます。ただし、デフォルトではなくて、任意で選択する機能として提供されます。
現在でも、以下のコマンドを使用することで、Turbopackを有効にした開発サーバーを起動することが可能です。
next dev --turbo
そして、公式ブログではTurbopackのベータ版とリリース候補版において、数千人もの開発者がテスト・問題報告、修正の確認に参加してくれたおかげで、最初のNext.js 15 RCリリース以降で54個のGitHub Issueが解決できたことに感謝の言葉を述べていました。
Turbopackチームは、過去3ヶ月間で特にコールドコンパイル(初めてコンパイルする処理)のパフォーマンス最適化に注力しており、以下のような改善がされました。
- メモリ使用量が25 – 35%削減
- 数千のモジュールを持つ大規模なページのコンパイル速度が30 – 50%向上
これらの領域は今後のリリースでもさらに、最適化が続けられる予定です。
さらに、Turbopackチームは現在、以下の機能の開発に注力しています。
- 永続的なキャッシング
- メモリ使用量のさらなる削減
next build
コマンドでのTurbopackの使用(現在、テストの96%がpassing)
Turbopackのサポートされている機能とサポートされていない機能についての詳細はこちら
バンドルツールに詳しくない私は、「結局、開発者はどうするべき?」って疑問に思ったので、調べてみました。
いまのNext.jsでも使用されているメジャーなバンドルツールwebpackは、実質終了しているみたいです。というのも、開発者がNext.jsの開発企業であるVercelに移籍しており、Turbopackの開発に取り組んでいるからです。
つまり、Turbopackはwebpackの後継であり、上位互換になっていくということですね。Next.jsの開発企業が開発しているので、これからのデフォルトはTurbopackになっていくことが分かります。
しかし、まだTurbopackは歴史が浅いものなので、一部のライブラリやツールと互換性がない可能性はありそうです。
以上のことより、「Turbopackはまだまだ移行段階であるから、見守っておく」と、今回私は結論づけたいと思います。
Async Request APIs(破壊的な変更)
Next.js 15では、リクエスト固有のデータ(ヘッダー、クッキー、パラメータ、検索パラメータなど)に依存するAPIを非同期化する破壊的な変更が導入されました。
この変更は、サーバーサイドレンダリング(SSR)のパフォーマンスと柔軟性を向上させることを目的とされています。
従来のSSRでは、サーバーはコンテンツをレンダリングする前にリクエストを待つ必要がありました。
しかし、すべてのコンポーネントがリクエスト固有のデータに依存しているわけではないです。なので理想は、サーバーがリクエストを受け取る前に可能な限り以下のような多くの準備を行うことです。
- 静的なコンポーネントの事前レンダリング:リクエスト固有のデータに依存しないコンポーネント(ヘッダー、フッターなど)を事前にレンダリングし、キャッシュをする。
- データフェッチの並列化:リクエスト非依存のデータ取得を事前に開始して、レンダリング時間を短縮する。
- リソースの事前ロード:必要なJavaScriptやCSSファイルを事前にロードして、初期表示を高速化する。
新しいアプローチとしてNext.js 15では、リクエスト固有のデータに依存するAPIを非同期化します。これにより、サーバーはリクエストを待つ必要がある場合とそうでない場合を区別できるようになります。
クッキーを取得する方法を例に、従来の方法と新しい方法を比較してみましょう。
// 従来の方法(同期的)
import { cookies } from 'next/headers';
export function AdminPanel() {
const cookieStore = cookies();
const token = cookieStore.get('token');
// ...
}
// 新しい方法(非同期)
import { cookies } from 'next/headers';
export async function AdminPanel() {
const cookieStore = await cookies();
const token = cookieStore.get('token');
// ...
}
主な違いは以下の点です。
AdminPanel
関数がasync
になっている。cookies()
の呼び出しにawait
が使用されている。
この変更により、Next.jsはリクエスト依存の処理と非依存の処理を区別して、可能な限り多くの作業をリクエストを受け取る前に行うことができるようになります。
この変更は互換性のないものであり、以下のAPIに影響します。
cookies
headers
draftMode
layout.js
,page.js
,route.js
,default.js
,generateMetadata
,generateViewport
内のparams
page.js
内のsearchParams
次のメジャーバージョンまでは、これらのAPIに同期的にアクセスできますが、開発環境と本番環境で警告が表示されます。
この移行に対応していくステップは、codemodで自動で行うのと資料を参照して手動で行う、2通りあります。
移行を自動化するためのcodemodが提供されています。以下のコマンドで実行できます。
npx @next/codemod/canary next-async-request-api .
基本的にcodemodで移行していくと思いますが、それでも完全にコードを移行できない場合は、アップグレードガイドが用意されています。
また、Next.jsアプリケーションを新しいAPIに移行する方法のサンプルも提供されています。
Server Actionsのセキュリティ強化
Next.js 15では、Server Actionsのセキュリティも強化されました。
Server Actionsは、クライアントサイドから呼び出すことができるサーバーサイド関数です。ファイルの先頭にuse server
ディレクティブを追加し、非同期関数をエクスポートすることで定義されます。
これまで、Server Actionやユーティリティ関数は、コード内で実際に使用されていなくても、パブリックにアクセス可能なHTTPエンドポイントとして露出していました。(意図していないことではなく、そういうものだった)
そこでNext.js 15では、以下の2つのセキュリティ強化が導入されました。
- デッドコードの除去:未使用のServer Actionsは、クライアントサイドのJavaScriptバンドルにIDが露出しなくなりました。これにより、バンドルサイズの削減とパフォーマンスの向上がされます。
- セキュアなアクションID:推測不可能な非決定的なIDを生成して、クライアントからのServer Action呼び出しを可能にしました。ビルド間で定期的にIDが再計算され、セキュリティが向上します。
以下は、Server Actionsの使用例と、新しいセキュリティ機能の動作を示しています。
// app/actions.js
'use server';
// このアクションは実際にアプリケーションで使用されている
// Next.jsはセキュアなIDを生成し、クライアントからの参照と呼び出しが可能
export async function updateUserAction(formData) {
// ユーザー更新のロジック
}
// このアクションはアプリケーションで使用されていない
// Next.jsは`next build`時にこのコードを自動的に削除し、パブリックエンドポイントを作成しない
export async function deleteUserAction(formData) {
// 未使用のユーザー削除ロジック
}
一つ注意点としては、Server Actionsは依然としてパブリックなHTTPエンドポイントとして扱う必要があることです。
まとめると、開発者にとっては実装方法を変える必要はなく今まで通りでいいけど、Next.jsの内部的には動きが変わっていて、セキュリティが向上したということですね。
Server Actionsのセキュリティについて詳しくは、こちらをご覧ください。
Static Route Indicator
Next.js 15では、開発環境において、ルートが静的か動的かを視覚的に確認できる「Static Route Indicator」が導入されました。
Static Route Indicatorとは、開発中にページがどのようにレンダリングされているかを直感的に理解するための指標です。開発者は各ルートのレンダリング方式を一目で確認できることが可能になります。
実際にNext.js 15の開発環境でStatic Route Indicatorを表示させてみた
実際にNext.js 15の環境で、以下のような静的と動的のファイルをそれぞれ作成しました。
// app/static-example/page.tsx
export default function StaticPage() {
return (
<div>
<h1>Static Route Example</h1>
<p>This page is static because it doesn't use any dynamic data or features.</p>
<ul>
<li>No use of params</li>
<li>No use of searchParams</li>
<li>No use of cookies or headers</li>
<li>No use of dynamic functions</li>
</ul>
</div>
);
}
// app/dynamic-example/page.tsx
import { cookies } from "next/headers";
export default async function DynamicPage() {
const cookieStore = await cookies();
const lastVisit = cookieStore.get("lastVisit")?.value || "Never";
return (
<div>
<h1>Dynamic Route Example</h1>
<p>This page is dynamic because it uses cookies.</p>
<p>Last visit: {lastVisit}</p>
<ul>
<li>Uses cookie data</li>
<li>This makes the page non-static</li>
</ul>
</div>
);
}
そして、next run dev
を実行して開発環境で表示させた結果はこのようになりました。
Staticなページの左下に雷のマークと”Static route”と書かれた指標が表示されました。一目で静的なルートであることがわかります。
Dynamicなページでは、”Dynamic route”と書かれるのかなと思ったのですが、そもそもの指標が表示されないようになっていました。
たしかに言われてみれば、タイトルが「Static Route Indicator」なので、表示されなかったわけではなく表示されないものみたいです。
この機能が必要なければ、next.config
の設定ファイルで表示されないようにすることもできます。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
devIndicators: {
appIsrStatus: false,
},
}
export default nextConfig
現段階では、Static routeしか表示されませんが、Vercelは「まだ第一歩に過ぎず、専用の開発者ツールも開発中で、詳細は近日中に公開します。」というようなことを言っています。
<Form>コンポーネント
Next.js 15で導入された新しい<Form>
コンポーネントは、HTMLの標準的な<form>
要素を拡張して、モダンなWeb開発に必要な機能を提供します。
特に検索フォームのような、新しいページへの遷移を伴うフォームの実装を大幅に簡素化します。
基本的な使用方法は、このようになります。
import Form from 'next/form';
export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
);
}
<Form>
コンポーネントには、以下の3つの機能が含まれています。
- Prefetching:フォームが表示されると、自動的にレイアウトとローディングUIがプリフェッチされて、フォームを送信する前から必要なリソースを読み込むことで、高速な遷移を実現する。
- Client-side Navigation:フォームの送信時に、共有レイアウトとクライアントサイドの状態を保持する。
- Progressive Enhancement:JavaScriptが読み込まれていない状況でも、通常のページ遷移として動作する。
従来、これらの機能を実現するには、複雑な実装が必要でした。以下は、新しい<Form>
コンポーネントを既存の<form>
コンポーネントで実装した場合の例です。
// 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>
コンポーネントを使用することで、このような複雑な実装が不要になりました。
<Form>
コンポーネントの詳細について、こちらをご覧ください。
next.config.tsのサポート
Next.js 15では、設定ファイルでTypeScriptがサポートされるようになりました。
従来のnext.config.mjs
に加えて、next.config.ts
が使用可能になり、型安全な設定オプションの記述が可能になりました。
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
/* 設定オプションをここに記述 */
};
export default nextConfig;
instrumentation.js(安定版)
Next.js 15で、サーバーサイドの監視機能を提供するinstrumentation.js
が正式に安定版としてリリースされました。よって、experimental.instrumentationHook
設定オプションも不要になります。
instrumentation.js
はregister()
APIを通じて、Next.jsのサーバー側のパフォーマンスの計測やエラーの原因の追跡、OpenTelemetryなどのライブラリとの統合ができます。
そして安定版になった上に、Sentryとの協力により、新しくonRequestError
フックが追加されました。これは、発生したエラーを検知して、そのエラー情報を任意のところに送れるというものです。
onRequestError
は、以下のようなサーバーサイドのエラーコンテキストを扱うことができます。
- ルーターの種類:
Page Router
またはApp Router
- サーバーコンテキスト:
Server Component
,Server Action
,Route Handler
,Middleware
onRequestError
の基本的な使用例は以下のようになります。
// instrumentation.js
export async function onRequestError(err, request, context) {
await fetch('https://.../report-error', {
method: 'POST',
body: JSON.stringify({
message: err.message,
request,
context
}),
headers: { 'Content-Type': 'application/json' },
});
}
export async function register() {
// 監視ツールのSDKの初期化処理
}
私はパフォーマンス計測やエラートラッキングの経験が全くないため、これらの機能の凄さがあまり分かりませんでしたが、Next.jsのバックエンドがより実務で使えるものになったということを感じました。
onRequestError関数の詳細については、こちらをご覧ください。
開発とビルドの改善
Next.js 15では、開発体験とビルドパフォーマンスの両面で改善が行われました。
サーバーコンポーネントのHMR最適化
開発時に、サーバーコンポーネントは保存時に再実行されます。これは、APIエンドポイントやサードパーティサービスへのfetch
リクエストも再度呼び出されるということです。
ローカル開発のパフォーマンスを向上させ、API課金による潜在的なコストを削減するため、ホットモジュールリプレイスメント(HMR)で以前のレンダリング時のfetch
レスポンスを再利用できるようになりました。
従来のNext.jsでは、以下のようなサーバーコンポーネントを開発する際、ファイルを保存するたびにAPIリクエストが発生していました。
async function WeatherWidget() {
const weather = await fetch('https://api.weather.com/tokyo', {
headers: { 'API-Key': process.env.WEATHER_API_KEY }
});
const data = await weather.json();
return (
<div className="weather-widget">
<h2>東京の天気</h2>
<p>気温: {data.temperature}℃</p>
<p>天気: {data.condition}</p>
</div>
);
}
このコンポーネントを編集して保存するたびに、新しいAPIリクエストが発生し、開発中に不必要なAPI使用量が積み重なってしまいます。
特に有料APIを使用している場合、無駄なコストが増えていくのは避けたいですよね。
Next.js 15では、HMRが最適化されて、以前のレンダリング時のfetch
レスポンスがキャッシュされます。そのため、スタイルの微調整やマークアップの変更時に、APIリクエストを再度実行する必要がなくなりました。
ただし、ナビゲーションまたはページ全体の再読み込み時には、キャッシュはクリアされるとのことです。
また、もしもこの機能が必要がない場合は、serverComponentsHmrCache
をfalse
に設定することで、HMRキャッシュを無効にすることができます。
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
serverComponentsHmrCache: false, // defaults to true
},
}
export default nextConfig
サーバーコンポーネントのHMRキャッシュの詳細については、こちらをご覧ください。
App Routerの静的生成の高速化
特に遅いネットワークリクエストを含むページのビルド時間を改善するために、静的生成が最適化されました。
これまでは、静的最適化プロセスでページを2回レンダリングしていました。1回目はクライアントサイドナビゲーション用のデータ生成、2回目は初期ページ訪問用のHTML生成の役割がありました。
現在は1回目のレンダリングを再利用して、2回目を省略することで、作業負荷とビルド時間を削減しています。
さらに、静的生成のワーカーがページ間でfetch
キャッシュを共有するようになりました。
fetch
呼び出しがキャッシュを無効化していない場合、同じワーカーが処理する他のページでその結果が再利用されます。これにより、同じデータに対するリクエスト数が削減されます。
高度な静的生成の制御(Experimental)
より細かい制御が必要な高度なユースケースのために、静的生成プロセスの制御に関する実験的なサポートが追加されました。
これらのオプションは並行処理の増加によりリソース使用量が増加して、メモリ不足エラーが発生する可能性があるため、特別な要件がない限り、現在のデフォルト設定の使用が推奨されています。
const nextConfig = {
experimental: {
// Next.jsがページ生成の失敗時に再試行する回数
// ビルド失敗までの試行回数
staticGenerationRetryCount: 1,
// ワーカーごとに処理されるページ数
staticGenerationMaxConcurrency: 8,
// 新しいエクスポートワーカーを起動する前の最小ページ数
staticGenerationMinPagesPerWorker: 25
},
}
export default nextConfig;
静的生成のオプションの詳細については、こちらをご覧ください。
セルフホスティング機能の改善
アプリケーションをセルフホスティングする際に、より制御がしやすくなりました。
Cache-Control制御の強化
アプリケーションをセルフホスティングする際に、Cache-Control
ディレクティブをより細かく制御できるようになりました。
ISRページが有効なページのCache-Controlヘッダーで、CDNが使用するstale-while-revalidate
の有効期限をカスタマイズできるようになりました。
// next.config.js
module.exports = {
// 1時間(秒単位)
expireTime: 3600,
}
この設定により、特定のrevalidate期間に応じて、Cache-Controlヘッダーの有効期限が計算されます。
例えば、あるパスのrevalidateが15分(900秒)で、expireTimeが1時間(3600秒)の場合、生成されるCache-Controlヘッダーは以下のようになります。
s-maxage=900, stale-while-revalidate=2700
これにより、stale-while-revalidate
が45分(2700秒)となり、設定されたexpireTime
よりも15分短い時間だけ古い状態を維持することができます。
さらに、Next.js 15はデフォルト値でカスタムのCache-Control
値を上書きしなくなりました。
// pages/api/custom-cache.js
export default function handler(req, res) {
res.setHeader('Cache-Control', 'public, max-age=3600');
// 以前:Next.jsのデフォルト値で上書きされていた
// 現在:開発者が設定した値が維持される
}
画像最適化の自動化
セルフホスティング環境での画像最適化が改善されました。
これまでは、Next.jsのサーバーで画像を最適化するためにsharp
のインストールを推奨されていましたが、インストールすることを忘れてしまう可能性がありました。
Next.js 15では、sharp
を手動でインストールする必要がなくなり、next start
やスタンドアロンモードで実行する際に、sharp
が自動的に利用できるようになりました。
これらの改善により、セルフホスティング環境でのNext.jsアプリケーションがより簡単になりました。
詳細については、Next.jsのセルフホスティングの新しいデモとチュートリアル動画をご覧ください。
ESLint 9 サポート
ESLint 8が2024年10月5日にEOL(サポート終了)を迎えることを受け、Next.js 15ではESLint 9のサポートを導入しました。
この移行をスムーズに行えるために、Next.jsはESLint 8と9の両方をサポートしており、開発チームは状況に応じて段階的に移行を進めることができます。
ESLint 9の最も大きな変更点は、ネストされた構造ではなく、フラットな配列構造が新しいデフォルトの設定形式となることです。Next.jsは、古い設定形式を使用していることを検知すると、自動的にESLINT_USE_FLAT_CONFIG=false
を適用し、従来の設定を継続して使用できるようにします。
next lint
コマンドでは、--ext
や--ignore-path
といった非推奨オプションが削除されます。注意点として、ESLint 10ではこれらの古い設定はサポートしなくなる予定です。
これらの変更の詳細については、移行ガイドを確認してください。
また、React Hooks関連の更新として、eslint-plugin-react-hooks
がv5.0.0にアップグレードされ、Hooksの使用に関する新しいルールが導入されました。これにより、より厳密なコードチェックが可能になります。
すべての変更点はeslint-plugin-react-hooks@5.0.0の変更履歴で確認することができます。
まとめ
本記事では、Next.js 15 RC2について、まとめていきました。
RC2での破壊的な変更はAsync Request APIs
のみであり、主にパフォーマンス改善、セキュリティ強化、開発体験・環境が取り扱われていたと思います。
個人的には、Static Route Indicator
の今後の進化が楽しみです!
私はまだまだNext.jsの知識が浅いですが、新しい情報をキャッチアップしていきたいと思います。
最後まで読んでいただき、ありがとうございました!
ぜひ、SNS等でシェアなどしていただけると、とても嬉しいです!