💡

Nuxt 3からNuxt 4へのメジャーアップデート:手順とハマりどころ、対応工数まとめ

に公開

はじめに

この記事は レバテック開発部 Advent Calendar 2025 12日目の記事です。

この記事では、Nuxt 3からNuxt 4へメジャーアップデートした際の手順と感想を紹介します!
(最近はNext.jsの記事が多く、Nuxtの人達はどこにいったんだろう...とふと思うことがありますw)

まだまだサポート期限まで余裕があると思いきや、EOL(2026-01-31)が意外と迫っていたため、早めの対応を行いました。(すぐにNuxt5も来そうで怖い....

この記事でわかること

  • Nuxt 4に上げた際の具体的な「ハマりどころ」
  • 手動でのディレクトリ移行手順
  • 実際のアップデート方法と対応工数

前提

  • 構成
    • Monorepo構成
    • 879コンポーネント、85ページ、約113,000行
  • 移行方針: 手動アップデート
    • 公式がCodemodsを提供していますが、Monorepoかつ独自のディレクトリ構成を採用していたため、手動での対応を選択しました。
  • fetch系の対応については事前に対応が済んでいるので触れません
  • (追記する可能性があります)

対象読者

  • Nuxt 4に上げたいが、工数が読めずなかなか着手できていない方
  • 破壊的変更がどれくらいあるのか、具体的に知りたい方

アップデート手順

基本的には公式の Upgrade Guide に沿って進めればOKですが、実戦でのポイントを補足します。

手順1:Nuxt 4本体のアップデート

まずはコマンドでアップグレードを実行します。

yarn nuxt upgrade

Nuxt 4に上げた後にtypecheckを実行すると、プロダクトによっては noUncheckedIndexedAccessエラーや、今まで検出されなかった型エラーが多数発生する可能性があります。

今回対応したプロダクトでは、合計360件程度のエラーが発生しました😭

  • noUncheckedIndexedAccess: 配列アクセス時のundefined考慮など。数が多すぎるため、今回はtsconfigfalseに設定し、一時的に回避(別タスクで対応予定)。
  • その他のエラー: 引数なし/ありイベントの混在など。こちらはロジック修正で対応。

まずはここでtypecheckのエラーを潰しきれば、第一段階はクリアです。

手順2:app ディレクトリへの移行(破壊的変更)

ここが最大の山場です。前提の通り、今回は手動でディレクトリ整理を行いました。
元々src配下などで管理していたcomponentsmiddlewareなどを、Nuxt 4推奨の app ディレクトリ構成へ移動させます。

▼ 変更後のディレクトリ構成イメージ

.
├── .output/
├── .nuxt/
├── app/                  <-- 新設(クライアントサイドのコードを集約)
│   ├── assets/
│   ├── components/
│   ├── composables/
│   ├── layouts/
│   ├── middleware/
│   ├── pages/
│   ├── plugins/
│   ├── utils/
│   ├── app.config.ts
│   ├── app.vue
│   └── router.options.ts
├── content/
├── layers/
├── modules/
├── node_modules/
├── public/
├── shared/               <-- 新設(Server/Client共有の型やUtils)
├── server/               <-- Nitroなどサーバーサイドコード
│   ├── api/
│   ├── middleware/
│   ├── plugins/
│   ├── routes/
│   └── utils/
└── nuxt.config.ts

⚠️ ここでの注意点(tsconfigの罠)

ディレクトリ移動時に大量のPathエラーが出ますが、焦ってコードを修正しないことが大切です!

今回のアップデートでtsconfigの生成ルールが変わり、役割ごとにファイルが分割されるようになりました。これにより、以前"extends": "./.nuxt/tsconfig.json"だけで効いていた型定義が効かなくなる可能性があります。

  • .nuxt/tsconfig.app.json: Vue コンポーネントなどアプリコード用
  • .nuxt/tsconfig.server.json: Nitro などサーバーコード用
  • .nuxt/tsconfig.node.json: ビルドタイム用(modules, nuxt.config.tsなど)
  • .nuxt/tsconfig.shared.json: アプリ・サーバー共有用
  • .nuxt/tsconfig.json: 後方互換用のレガシー設定

また、ServerとClient側で共通して使用していた型定義ファイルなどは、新設されたshared/配下に移行しました。

Use the shared/ directory to share functionality between the Vue app and the Nitro server.

引用:https://nuxt.com/docs/4.x/directory-structure/shared#:~:text=Use the shared/ directory to share functionality between the Vue app and the Nitro server.

手順3:Config周り&テスト修正

ディレクトリ構造の変更に伴い、設定ファイルのパス修正を行います。

  1. パスの修正:

    • .storybook/main.tsのパス修正
    • nuxt.config.tssrcDirを修正
    • tests ディレクトリのパス解決(nuxt.config.tstsConfigオプションで拡張)
  2. tsconfig設定の移行:

    • これまでtsconfig.jsonに直接書いていた拡張設定は非推奨となりました。今後は nuxt.config.ts経由で設定します。

We do not recommend modifying the contents of this file directly... Instead, extend it via nuxt.config.ts.

引用:https://nuxt.com/docs/4.x/directory-structure/tsconfig#:~:text=}-,We do not recommend modifying the contents of this file directly,.,-Read more about

ハマりポイント

今回の対応で最も伝えたい教訓はこれです!

「appディレクトリ変更時に出る型エラーは、コード修正の前に tsconfig周りを見直せ!」

公式には Impact Level: Minimal(影響レベル:最小) と書いてありますが、実際の影響は結構大きかったです(笑)。

私はディレクトリ変更時に発生した大量の型エラーを、コード側で一つずつ修正しようとしてしまい、結果的に後戻り作業が発生しました。実はtsconfig.json周りの設定を公式ドキュメントベースに正しく修正すれば、それだけでエラーの殆どが解消しました。

srcをパス指定している箇所はプロダクトによって異なると思うので、まずは設定周りを疑って確認しましょう。(公式ドキュメントは何度でも読もう)

Monorepo・大規模開発での進め方

一度にすべてやろうとせず、以下のサブタスクに切って進めることで、早期解決が図れました。

  1. Nuxt 4アップデート
  2. appディレクトリへの変更
  3. Config周りの修正
  4. テスト修正(Snapshot更新含む)

こまめにcommitし、typecheckを回すのがおすすめです。

既存開発との兼ね合い(ブランチ戦略)

「機能開発を止めてメジャーアップデート対応だけ行う」というのは現実的に難しいですよね。私のチームでも機能開発が進行中だったため、以下のようなフローで進めました。

  1. developからfeature/nuxt_4ブランチを切る。
  2. 既存開発のコンポーネント実装などは、完了次第developにマージしていく。(※細かいブランチ運用については触れません)
  3. feature/nuxt_4は、最後にdevelopを取り込みコンフリクトを解消する。
    • 作業は基本的に「appディレクトリに入れ直すだけ」なので、コンフリクト解消の負担は少ない。
  4. テストを行い、問題なければ本番リリース。
  5. その後、既存機能の画面構築フェーズへ入る。

万が一不具合があった場合の切り戻しにも対応しつつ、既存の機能も開発できるような進め方にしました!(本来であれば、既存で進めている機能を全てリリースしきるか、別の機能開発が進んでいない時にアップデートした方が良いかと思いますが、開発状況的にはこの対応がベストだと判断しました。)

対応工数

プロダクトの規模や実装状況にもよりますが、アップデート作業自体は2日(実働 10時間強)で完了しました。
※ ここで言う「完了」とは、storybook起動、typecheck、lint、コンポーネントテスト、build が正常に通る状態を指します(本格的なQA工数は含んでいません)。

比較的スムーズに進んだ要因として、事前にチームで「Upgrade Guideの読み合わせ会」を行っていたことが挙げられます。これにより、着手前に主な変更点と影響範囲のイメージをチーム内で共有できていたのが大きかったです。

今回はMonorepo構成で大きく2箇所の対応が必要だったことや、他のタスクの合間に進めたことを考慮すると、構成によっては1日で作業が終わるチームもあるのではないかと思います。

まとめ

今回のアップデートは、既存の型定義の甘い部分が浮き彫りになるような、「厳格化」を感じるものでした。しかし、ある程度の互換性を保ったまま移行できたので、過去のNuxt(Vue)の反省が活かされていると感じます。

Nuxt 4へのアップデートを検討中の方、ライブラリやフレームワークのアップデートは計画的に行いましょう!

レバテック開発部

Discussion