Vue.jsベースのフルスタックフレームワークとして広く採用されているNuxtが、2025年7月15日にバージョン4.0をリリースしました。約1年のテスト期間を経た安定性重視のメジャーアップデートであり、開発体験(DX)の向上に重点が置かれています。
Nuxt 4の中心的な設計方針は「Nuxt 3からの移行をスムーズにしつつ、プロジェクト構造・型安全性・パフォーマンスを底上げする」ことです。多くの破壊的変更はNuxt 3.xの段階で互換フラグとしてテストできる状態が1年以上続いていたため、段階的に対応できます。
ここでは、v4.0からv4.3までに導入された主要な機能変更を体系的に整理し、Nuxt 3プロジェクトからの移行判断に必要な情報をまとめています。
app/ディレクトリを軸にしたプロジェクト構成の再設計
Nuxt 4で最も目立つ変更が、プロジェクトのソースコードをapp/ディレクトリに集約する構成への移行です。
Nuxt 3ではcomponents/、pages/、composables/、layouts/などをプロジェクトルート直下に配置していました。Nuxt 4ではこれらをapp/ディレクトリの配下に移動させ、設定ファイルやserver/ディレクトリとアプリケーションコードを明確に分離します。
# Nuxt 3の構成
my-project/
├── components/
├── composables/
├── layouts/
├── pages/
├── server/
├── nuxt.config.ts
└── app.vue
# Nuxt 4の構成
my-project/
├── app/
│ ├── components/
│ ├── composables/
│ ├── layouts/
│ ├── pages/
│ └── app.vue
├── server/
├── shared/
│ └── utils/
└── nuxt.config.ts
この変更によって得られる利点は主に3つあります。
- 関心の分離が明確になる: クライアント寄りのコードと、サーバー側のコード、プロジェクト設定が物理的に分かれるため、大規模プロジェクトでのナビゲーションが容易になります
- モノレポ構成との親和性: ルート直下にワークスペースの設定ファイルを置いても、アプリケーションコードと混在しません
- shared/ディレクトリによるコード共有: 後述するshared/ディレクトリと合わせて、サーバーとクライアントで共通利用するロジックの管理が整理されます
なお、Nuxt 4でもルート直下にソースファイルを置く従来のレイアウトは引き続き動作します。app/ディレクトリが存在しない場合、Nuxt 4は自動的にルート直下をソースディレクトリとして認識します。
shared/ディレクトリ — サーバーとクライアントの共通コード置き場
Nuxt 4で新設されたshared/ディレクトリは、app/(クライアント側)とserver/の両方からインポートできる共有コードの格納場所です。
shared/
├── types/
│ └── user.ts
├── utils/
│ └── format-date.ts
└── constants.ts
shared/utils/とshared/types/に配置したファイルはNuxtによって自動インポートの対象になります。バリデーションルール、定数定義、型定義など、サーバーとクライアントで重複しがちなコードを一箇所にまとめられるため、コードの一貫性が保ちやすくなります。
// shared/utils/validate-email.ts
export function validateEmail(email: string): boolean {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
// app/pages/register.vue — クライアント側で利用
const isValid = validateEmail(form.email)
// server/api/users.post.ts — サーバー側でも同じ関数を利用
const isValid = validateEmail(body.email)
データフェッチの挙動変更 — useFetchとuseAsyncData
Nuxt 4ではデータ取得コンポーザブルの内部挙動が見直されています。既存のAPIシグネチャ自体に大きな変更はありませんが、デフォルト値やリアクティビティの扱いが変わっているため、移行時に確認が必要です。
初期値がundefinedに統一
Nuxt 3ではuseFetchやuseAsyncDataのdataの初期値がnullでしたが、Nuxt 4ではundefinedに変更されました。テンプレート内でv-if="data"のような判定をしている箇所は影響を受けませんが、data === nullで明示的に未取得状態を判定しているコードは修正が必要です。
// Nuxt 3
const { data } = await useFetch('/api/users')
// data.value は取得前に null
// Nuxt 4
const { data } = await useFetch('/api/users')
// data.value は取得前に undefined
dataがshallowRefに変更
useFetch/useAsyncDataの戻り値dataがrefからshallowRefに変更されました。ネストの深いオブジェクトの内部プロパティを直接書き換えてもリアクティブに反応しなくなります。
// Nuxt 4ではこの書き方でUIが更新されない
data.value.users[0].name = '新しい名前'
// 代わりにdata.value全体を差し替える
data.value = {
...data.value,
users: data.value.users.map((u, i) =>
i === 0 ? { ...u, name: '新しい名前' } : u
)
}
パフォーマンスの観点では、大量のデータを扱う画面でのリアクティビティコストが下がるメリットがあります。
dedupeオプションの仕様変更
useFetch/useAsyncDataのdedupeオプションが、boolean値(true/false)から文字列値('cancel'/'defer')に変更されました。dedupe: trueはdedupe: 'cancel'に、dedupe: falseはdedupe: 'defer'に対応します。デフォルト値は'cancel'で、同じキーに対する新しいリクエストが発行されると進行中の古いリクエストが自動的にキャンセルされます。
pendingプロパティの計算方法の変更
pendingプロパティがcomputedプロパティに変更され、statusがpendingの場合にのみtrueを返すようになりました。immediate: falseを指定した場合、初回リクエストが発行されるまでpendingはfalseです。
AbortControllerによるキャンセル制御(v4.2〜)
v4.2でuseAsyncDataのハンドラにAbortControllerのsignalが渡されるようになりました。$fetchなどキャンセル可能なリクエストに対して、きめ細かいキャンセル制御が可能です。
const { data } = await useAsyncData('users', ({ signal }) => {
return $fetch('/api/users', { signal })
})
refreshやexecuteにもAbortControllerのsignalを直接渡せるため、ユーザー操作やコンポーネントのライフサイクルに応じたリクエスト中断が実装しやすくなっています。
TypeScript体験の底上げ
Nuxt 4ではTypeScriptの型推論が複数の面で改善されています。
より厳密な型チェック
nuxt.config.tsのtypescript.strictがデフォルトでtrueになりました。noUncheckedIndexedAccessなど、より厳格な型チェックオプションがデフォルトで有効化されます。
ルートパラメータの型推論
pages/ディレクトリのファイル名からルートパラメータの型が自動生成されるようになりました。useRouteで取得するパラメータに型が付くため、存在しないパラメータへのアクセスがコンパイル時に検出できます。
// pages/users/[id].vue の場合
const route = useRoute()
route.params.id // string 型として推論される
route.params.name // コンパイルエラー(存在しないパラメータ)
実験的TypeScriptプラグイン(v4.2〜)
v4.2でVSCode/エディタ向けのTypeScriptプラグインが実験的に導入されました。nuxt.config.tsのモジュール設定やランタイム設定に対する型補完・バリデーションが強化されます。
開発サーバーとCLIの高速化
Nuxt 4では開発時の体感速度を向上させるために、3つの最適化が実施されています。
| 最適化項目 | 仕組み | 効果 |
|---|---|---|
| V8コンパイルキャッシュの再利用 | Node.jsのバイトコードキャッシュを次回起動時に再利用 | コールドスタートの短縮 |
| fs.watch APIへの切り替え | ポーリングではなくOSネイティブのファイル監視を利用 | CPU・メモリ使用量の低減 |
| ソケットベースの内部通信 | CLI→Vite devサーバー間をネットワークポートからソケットに変更 | 特にWindows環境でのオーバーヘッド削減 |
これらの変更は設定不要で自動的に適用されます。
Import Mapsによるビルドの安定性向上(v4.1〜)
v4.1で導入されたimport maps機能は、ビルド出力のチャンクハッシュ安定性を改善します。
エントリーチャンクの変更が、直接依存していない他のチャンクのハッシュに伝播しなくなります。これにより、CDNキャッシュやブラウザキャッシュの効率が向上し、小さなコード変更時にすべてのチャンクが再ダウンロードされる問題を軽減します。
この機能はHTML内に<script type="importmap">を挿入することで実現されており、import mapsをサポートしていないブラウザがビルドターゲットに含まれる場合は自動的に無効化されます。
Unhead v2とCapo.jsによるhead最適化
Nuxt 4ではheadタグ管理ライブラリ「Unhead」がv2にアップグレードされています。主な変更点は以下のとおりです。
- Capo.jsによるタグ順序の最適化がデフォルトで有効:
<head>内の要素を、ページの体感速度が最も向上する順序に自動で並べ替えます - 非推奨プロパティの削除:
vmid、hid、children、bodyなどの旧来のプロパティが削除されました - Promiseを入力として渡す方法が非サポートに: 直接的な値またはリアクティブな値を渡す必要があります
旧来のプロパティを使用しているプロジェクトでは、unheadモジュールのレガシー互換モードを有効にすることで段階的に移行できます。
Rolldown(実験的サポート)
Rollup互換のRust製バンドラ「Rolldown」が、v4.1からViteの差し替え(rolldown-vite)として実験的に利用できます。ビルド速度の大幅な向上が見込まれますが、プロダクション利用にはまだ制約があります。
// nuxt.config.ts(Rolldown有効化の一例)
export default defineNuxtConfig({
vite: {
// rolldown-viteをoverrideとして導入した場合、
// Nuxtが自動検出してビルド設定を調整
}
})
Rolldownが利用可能になると、Nuxtは自動的に検出し、適切なビルド設定に切り替えます。
コンポーネント名の正規化
Nuxt 4ではcomponents/ディレクトリ内のコンポーネント名が、ディレクトリ名とファイル名をPascalCaseで結合した形に正規化されます。
app/components/
├── ui/
│ └── Button.vue → <UiButton />
└── form/
└── Input.vue → <FormInput />
Nuxt 3ではコンポーネント名の生成ルールにゆらぎがあり得ましたが、Nuxt 4では一貫したルールで名前が決定されるため、大規模プロジェクトでの名前衝突リスクが低減します。
Nuxt 3から4への移行手順
Nuxt 4はNuxt 3からの段階的な移行を想定して設計されています。多くの破壊的変更はNuxt 3.x時点で互換フラグ経由でテスト可能でした。
手順1: パッケージのアップグレード
npx nuxt upgrade
手順2: 自動マイグレーションツールの実行
npx codemod nuxt/4/migration-recipe
codemods(自動コード変換ツール)が提供されており、ディレクトリ構造の移動やAPIの書き換えを半自動で実行できます。
手順3: 動作確認と調整
アップグレード後に確認すべき主なポイントは以下のとおりです。
| 確認項目 | チェック内容 |
|---|---|
| データフェッチの初期値 | data === null判定をdata == nullまたは!dataに変更 |
| shallowRefの影響 | ネストオブジェクトの直接変更箇所を、値の差し替えに修正 |
| Unhead v2の非推奨API | hid、vmid、children、bodyの使用箇所を修正 |
| モジュール互換性 | 使用中のサードパーティモジュールがNuxt 4対応か確認 |
| TypeScript厳格モード | noUncheckedIndexedAccessで新たに発生する型エラーへの対処 |
事前検証: Nuxt 3の互換フラグを利用する方法
Nuxt 3.x系でも以下の設定で、Nuxt 4の挙動を事前にテストできます。
// nuxt.config.ts(Nuxt 3.x)
export default defineNuxtConfig({
future: {
compatibilityVersion: 4
}
})
この設定により、Nuxt 4のデフォルト挙動(ディレクトリ構成、データフェッチのデフォルト値など)がNuxt 3環境で有効化されます。段階的に問題を洗い出せるため、大規模プロジェクトでの安全な移行に有効です。
v4.0からv4.3までのリリース概要
| バージョン | リリース時期 | 主な追加機能 |
|---|---|---|
| v4.0 | 2025年7月 | app/ディレクトリ構成、shared/ディレクトリ、データフェッチ改善、TypeScript強化、Unhead v2、CLI高速化 |
| v4.1 | 2025年9月 | Import Mapsによるビルド安定性向上、Rolldown実験的サポート、モジュール作者向けonInstall/onUpgradeフック |
| v4.2 | 2025年10月頃 | AbortControllerによるデータフェッチキャンセル、開発時エラーオーバーレイ、実験的TypeScriptプラグイン |
| v4.3 | 2026年1月頃 | レイアウト機能拡張、キャッシュ戦略強化、エラーオーバーレイのドラッグ・最小化対応、パフォーマンス改善 |
Nuxt 5への先行テスト(compatibilityVersion: 5)
Nuxt 4.2以降ではfuture.compatibilityVersionを5に設定することで、開発中のNuxt 5の破壊的変更を先行テストできます。
export default defineNuxtConfig({
future: {
compatibilityVersion: 5
}
})
現時点で有効化される主な変更として、Vite 6のEnvironment APIへの移行があります。開発サーバーが複数の環境(クライアント・サーバー)を並行管理する仕組みが刷新され、開発時と本番時の挙動差が縮小されます。
Nuxt 4とNext.jsの位置づけの違い
フロントエンドフレームワーク選定時に比較されることが多いNext.jsとの違いを整理します。
| 比較軸 | Nuxt 4 | Next.js(App Router) |
|---|---|---|
| ベースライブラリ | Vue 3 | React |
| レンダリング戦略 | SSR / SSG / CSR / ハイブリッドをルートごとに選択可能 | SSR / SSG / CSR / ISR をルートごとに選択可能 |
| サーバーエンジン | Nitro(h3ベース、エッジ対応) | Node.js / Edge Runtime |
| データフェッチ | useFetch / useAsyncData(コンポーザブル方式) | Server Components / fetch(コンポーネント内直接) |
| 型安全ルーティング | ファイル名ベースで自動生成 | 手動定義が基本 |
| バンドラ | Vite(Rolldown実験対応) | Turbopack(実験対応)/ Webpack |
Vue.jsエコシステムを採用しているプロジェクトにとって、Nuxt 4はVue 3との統合が最も深いフレームワークです。Nitroによるエッジデプロイ対応やViteとの統合による高速な開発サーバーは、同エコシステム内でのアドバンテージになります。
まとめ
Nuxt 4はNuxt 3からの進化として、以下の領域を重点的に改善したリリースです。
- プロジェクト構造:
app/ディレクトリへの集約とshared/ディレクトリの新設により、コードの整理と共有が容易に - データフェッチ:
shallowRef化によるパフォーマンス向上、AbortControllerによるキャンセル制御の追加 - TypeScript: 厳格モードのデフォルト化、ルートパラメータの自動型推論
- パフォーマンス: CLI・開発サーバーの高速化、Import Mapsによるビルドキャッシュ効率の向上
- head管理: Unhead v2 + Capo.jsによる体感速度の最適化
Nuxt 3からの移行は、互換フラグによる事前検証とcodemodツールの活用で段階的に進められます。新規プロジェクトであればNuxt 4から開始し、既存プロジェクトではfuture.compatibilityVersion: 4で事前にテストしてから移行するアプローチが推奨されます。
公式の移行ガイドは Nuxt Upgrade Guide で確認できます。