Nuxt 4の新機能を徹底解説:ディレクトリ構造の刷新とNuxt 3からの移行ガイド(2026年版)

Nuxt 4で導入されたapp/ディレクトリ構造、シングルトンデータフェッチ層、shallow reactivity、TypeScript分割コンテキストについて、コード例を交えて詳しく解説します。

Nuxt 4 directory structure and migration guide

Nuxt 4は、アプリケーションコードと設定ファイルを明確に分離する新しいディレクトリ構造を導入しました。2025年7月にリリースされ、2026年4月時点でバージョン4.4に到達したこのメジャーアップデートは、シングルトンデータフェッチ層、shallowReactivityのデフォルト化、TypeScriptコンテキストの分割など、開発体験を大幅に改善する機能を備えています。Nuxt 2からNuxt 3への移行と比較すると、今回のアップグレードパスは格段にスムーズです。

自動マイグレーションツール

NuxtチームはCodemodと連携し、ほとんどの移行ステップを自動化しています。npx codemod@latest nuxt/4/migration-recipe を実行すると、ディレクトリ再構築、データフェッチの更新、非推奨APIの置換が自動的に処理されます。

Nuxt 4のapp/ディレクトリ構造

Nuxt 4では、すべてのアプリケーションソースコードがデフォルトでapp/ディレクトリに配置されます。この変更は実際の開発上の課題を解決するものです。LinuxやWindowsのファイルウォッチャーは、アプリケーションコードがnode_modules/.git/、設定ファイルと混在せず、専用のサブディレクトリに格納されている場合、大幅にパフォーマンスが向上します。

新しいディレクトリレイアウトは以下の通りです。

text
my-nuxt-app/
├─ app/
│  ├─ assets/
│  ├─ components/
│  ├─ composables/
│  ├─ layouts/
│  ├─ middleware/
│  ├─ pages/
│  ├─ plugins/
│  ├─ utils/
│  ├─ app.vue
│  ├─ app.config.ts
│  └─ error.vue
├─ content/
├─ public/
├─ shared/         # 新規:appとserver間で共有するコード
├─ server/
└─ nuxt.config.ts

shared/ディレクトリは特筆すべき追加要素です。shared/に配置されたcomposableやユーティリティは、VueアプリとNitroサーバーの両方で自動インポートされます。バリデーションスキーマ、型定義、ユーティリティ関数をコンテキスト間で共有する際に、手動のimport文を記述する必要がなくなります。

Nuxt 3からNuxt 4へのステップバイステップ移行

アップグレードプロセスは1つのコマンドから始まります。Nuxtは既存のフラット構造を検出し、変更なしで動作を継続するため、段階的な移行が可能です。

bash
# Nuxtのアップグレードと依存関係の重複解消
npx nuxt upgrade --dedupe

パッケージのアップグレード後、アプリケーションファイルをapp/ディレクトリに移動します。

bash
# ディレクトリ再構築の自動化
npx codemod@latest nuxt/4/file-structure

このcodemodにより、assets/components/composables/layouts/middleware/pages/plugins/utils/app.vueerror.vueapp.config.tsapp/に移動されます。ルートに残すべきファイル(nuxt.config.tsserver/public/content/)はそのまま維持されます。

ディレクトリ再構築をすぐに行わない場合は、ソースディレクトリを明示的に設定できます。

nuxt.config.tstypescript
export default defineNuxtConfig({
  srcDir: '.',
  dir: { app: 'app' },
})

この設定により、Nuxt 4はプロジェクトルートからファイルを解決し、Nuxt 3と同じ動作を維持します。

シングルトンデータフェッチ層とリアクティブキー

Nuxt 4では、useAsyncDatauseFetchのデータ管理方法が根本的に変更されました。同じキーを使用する複数のコンポーネントは、独立したコピーを保持する代わりに、単一のリアクティブ参照を共有するようになります。

app/composables/useProductData.tstypescript
export function useProductData(productId: string) {
  return useAsyncData(
    `product-${productId}`,
    () => $fetch(`/api/products/${productId}`),
    {
      getCachedData: (key, nuxtApp, ctx) => {
        // ctx.causeでフェッチの理由を判別可能
        if (ctx.cause === 'refresh:manual') return undefined
        return nuxtApp.payload.data[key]
      },
    },
  )
}

この新しいデータ層には3つの重要な変更点があります。

  • 共有ref: 2つのコンポーネントでuseProductData('abc')を呼び出すと、同じdataerrorstatusのrefが返されます。一方を更新すると、もう一方にも即座に反映されます。
  • 自動クリーンアップ: あるキーを使用する最後のコンポーネントがアンマウントされると、関連データがメモリから解放されます。
  • リアクティブキー: キーをcomputedまたはrefでラップすると、値の変更時に自動的にデータが再フェッチされます。

getCachedDataコールバックは、causeプロパティ('initial''refresh:hook''refresh:manual''watch')を持つコンテキストオブジェクトを受け取るようになり、キャッシュデータの提供と新規フェッチの使い分けをきめ細かく制御できます。

デフォルト値の変更

useAsyncData/useFetchのdataとerrorのデフォルト値がnullからundefinedに変更されました。=== nullチェックを=== undefinedに更新するか、緩い等価演算子を使用する必要があります。

Shallow Reactivityによるパフォーマンス最適化

Nuxt 4では、useAsyncDatauseFetchdatarefではなくshallowRefをデフォルトで使用するようになりました。Vueはネストされたプロパティを再帰的に追跡しなくなるため、深くネストされたオブジェクトや大規模な配列を含むAPIレスポンスに対して、計測可能なパフォーマンス改善が得られます。

app/pages/dashboard.vuetypescript
<script setup lang="ts">
// dataはデフォルトでshallowRefになる
const { data: metrics } = await useFetch('/api/dashboard/metrics')

// 直接的なプロパティ変更はリアクティビティをトリガーしない
// metrics.value.visits = 100  // 再レンダリングが発生しない

// 値全体を置換すると更新がトリガーされる
metrics.value = { ...metrics.value, visits: 100 }

// 特定の呼び出しでディープリアクティビティを有効化
const { data: settings } = await useFetch('/api/settings', {
  deep: true,
})
</script>

ダッシュボード、商品一覧、記事ページなど、ほとんどの読み取り専用データ表示では、shallow reactivityはコード変更なしで動作します。フォームやネストされたプロパティを直接変更するインタラクティブUIには、deep: trueオプションが引き続き利用できます。

Vue.js / Nuxt.jsの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

TypeScriptコンテキスト分割と型安全性の強化

Nuxt 4では、プロジェクト内の各コンテキストに対して個別のTypeScript設定が生成されます。

  • .nuxt/tsconfig.app.json — Vueアプリケーションコード用
  • .nuxt/tsconfig.server.json — Nitroサーバーコード用
  • .nuxt/tsconfig.shared.json — 共有ユーティリティ用
  • .nuxt/tsconfig.node.json — ビルド時設定用

この分離により、IDEがクライアントコード内でサーバー専用APIを提案したり、その逆が起きたりすることがなくなります。プロジェクトルートのtsconfig.jsonは、4つすべての設定を参照する構成になります。

json
{
  "files": [],
  "references": [
    { "path": "./.nuxt/tsconfig.app.json" },
    { "path": "./.nuxt/tsconfig.server.json" },
    { "path": "./.nuxt/tsconfig.shared.json" },
    { "path": "./.nuxt/tsconfig.node.json" }
  ]
}

CIでの型チェックも変更があります。vue-tscコマンドは、プロジェクト参照を正しく処理するために-bフラグ(ビルドモード)が必要になります。

bash
# 変更前(Nuxt 3)
nuxt prepare && vue-tsc --noEmit

# 変更後(Nuxt 4)
nuxt prepare && vue-tsc -b --noEmit

もう1つのTypeScriptの変更点として、compilerOptions.noUncheckedIndexedAccessがデフォルトでtrueに設定されるようになりました。配列要素やオブジェクトプロパティへのインデックスアクセスはT | undefinedを返すようになり、コンパイル時に潜在的なランタイムエラーを検出できます。

コンポーネント名の正規化とVue Router v5

Nuxt 4.3ではVue Router v5にアップグレードされ、unplugin-vue-routerへの依存が解消されました。ほとんどのアプリケーションでは、このアップグレードは透過的に行われます。

コンポーネントの命名規則が標準化されました。components/dashboard/MetricsCard.vueにあるコンポーネントは、<KeepAlive>、Vue DevTools、テストユーティリティを含むすべてのコンテキストで一貫してDashboardMetricsCardという名前が付けられます。

vue
<!-- app/pages/dashboard.vue -->
<template>
  <NuxtPage :keepalive="{
    include: ['DashboardMetricsCard', 'DashboardRecentActivity'],
  }" />
</template>

コンポーネント名フィルターを使用した<KeepAlive>を利用しているプロジェクトでは、この新しい命名規則に合わせた更新が必要です。コンテキストによって名前が異なる以前の動作は適用されなくなりました。

Head管理における破壊的変更への対応

Nuxt 4にはUnhead v2が同梱されており、useHeaduseSeoMetaからいくつかの非推奨プロパティが削除されています。

app/pages/product/[id].vuetypescript
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

// Unhead v2:vmid、hid、children、bodyプロパティが削除
useSeoMeta({
  title: () => product.value?.name ?? 'Product',
  ogTitle: () => product.value?.name ?? 'Product',
  description: () => product.value?.description ?? '',
  ogImage: () => product.value?.imageUrl ?? '',
})
</script>

テンプレートパラメータやエイリアスソートに依存しているプロジェクトでは、これらを明示的なプラグインとしてインストールする必要があります。

app/plugins/unhead.tstypescript
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup() {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})
Nuxt 3のサポート期間

Nuxt 3は2026年7月31日までセキュリティアップデートと重要なバグ修正を受け続けます。その日以降、Nuxt 3はサポート対象外となります。EOLフレームワーク上で本番アプリケーションを運用することを避けるため、今のうちに移行計画を立てることが推奨されます。

移行チェックリストと注意すべきポイント

自動codemodがほとんどの変更を処理しますが、手動対応が必要な項目もあります。

  • window.__NUXT__の削除: useNuxtApp().payloadに置き換えが必要です。このグローバルオブジェクトはNuxt 4ではハイドレーション後に削除されます。
  • pages:extendフック: ページメタスキャン後に実行される新しいpages:resolvedフックに切り替えが必要です。
  • dedupeブーリアン: refresh({ dedupe: true })refresh({ dedupe: 'cancel' })に、false'defer'に置き換えます。
  • インラインスタイル: デフォルトではVueコンポーネントスタイルのみがインライン化され、グローバルCSSは別ファイルとして読み込まれます。Nuxt 3の動作に戻すにはfeatures: { inlineStyles: true }を追加します。
  • clearNuxtState: undefinedではなく初期値にリセットされるようになりました。以前の動作が必要な場合はclearNuxtState('key', { reset: false })を使用します。

本番アプリケーション向けの実践的な移行手順は以下の通りです。

  1. npx nuxt upgrade --dedupeを実行し、アプリケーションのビルドを確認
  2. codemodを実行: npx codemod@latest nuxt/4/migration-recipe
  3. ファイルをapp/に移動(file-structure codemodにより自動化)
  4. データフェッチロジックのnullチェックをundefinedに更新
  5. 正規化された名前で<KeepAlive>コンポーネントをテスト
  6. CIの型チェックをvue-tsc -b --noEmitに更新
  7. テストスイート全体を実行し、noUncheckedIndexedAccessによって検出されたTypeScriptエラーを修正

VueとNuxtの概念をより深く理解するには、SharpSkillのVue/Nuxt面接対策問題を参照するか、Nuxt 4にも引き継がれるレンダリング戦略の背景としてSSRと静的生成ガイドを確認してください。

まとめ

  • Nuxt 4.4(2026年4月時点の最新版)は、app/ディレクトリ構造、シングルトンデータフェッチ、分割TypeScriptコンテキストを本番環境対応のデフォルトとして確立しています
  • npx codemod@latest nuxt/4/migration-recipeコマンドにより、ディレクトリ再構築、非推奨APIの置換、データフェッチの更新が自動化されます
  • shallowRefによるshallow reactivityは、ほとんどのケースでコード変更不要のままパフォーマンスを改善します
  • コンテキストごとの個別TypeScript設定(app、server、shared、node)により、コンテキスト間の型リークが解消され、IDEの補完精度が向上します
  • Nuxt 3は2026年7月31日にサポート終了を迎えるため、それ以前の移行がサポート対象バージョンの維持に必要です
  • Vue Router v5とUnhead v2はより整理されたAPIを提供しますが、非推奨プロパティの削除に伴い移行時の確認が求められます

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#nuxt
#vue
#migration
#typescript
#tutorial

共有

関連記事