サンプルプロジェクトを作ってVite 7からVite 8への移行手順を確認してみた

サンプルプロジェクトを作ってVite 7からVite 8への移行手順を確認してみた

2026.04.23

どうも!オペ部の西村祐二です!

2026年3月12日に Vite 8.0 がリリースされました。公式アナウンスでは「多くのプロジェクトはスムーズに移行できる」とされていますが、実際に手を動かすとどのあたりで引っかかるのかが気になったので、Vite 7 のサンプルプロジェクトを作って Vite 8 へ上げる流れを確認しました。手元で試して確認した内容をまとめます。

Vite 8 での主な変更点

公式の移行ガイドによると、Vite 8 では以下のような変更が入っています。

  • build.rollupOptions.output.manualChunks のオブジェクト形式が削除され、関数形式も非推奨(代替は output.codeSplitting
  • JavaScript の変換が esbuild から Oxc に切り替え(JSX設定オプションなどは自動変換)
  • transformWithEsbuild も非推奨化(代替は transformWithOxc
  • build.rollupOptionsbuild.rolldownOptions にリネームされ非推奨化(当面は利用可能だが将来削除予定、Vite 8 では Rolldown ベースに移行中)
  • ブラウザターゲットの引き上げ(Chrome 107→111、Firefox 104→114、Safari 16.0→16.4、Edge 107→111)

ドキュメントを眺めるだけだと影響範囲がつかみにくかったので、最小構成と少し実務寄りの構成、それに加えて非推奨化された各オプションの挙動もあわせて確認しました。

試してみる

環境

  • Node.js v25.8.2
  • pnpm 10.33.0
  • Vite 7.3.28.0.10

1. 素のテンプレートで移行を試す

Vite 7 の vanilla-ts テンプレートを /tmp に作成し、まずは Vite 7 のままビルドできることを確認します。

cd /tmp
pnpm create vite@7 vite7-to-vite8-sample --template vanilla-ts
cd vite7-to-vite8-sample
pnpm install
pnpm build

この時点では Vite 7.3.2 で問題なくビルドできました。続けて Vite 8 に上げます。

pnpm up vite@^8
pnpm build

素のテンプレートのままであれば、Vite 8.0.10 でもビルドは通りました。最小構成であれば Vite 8 への更新自体で引っかかる要素は少なそうでした。

2. manualChunks のオブジェクト形式を追加した場合

実案件を想定して、vite.config.tsbuild.rollupOptions.output.manualChunks をオブジェクト形式で追加しました。古くから運用しているプロジェクトではこの形が残っているケースがあります。

vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['typescript'],
        },
      },
    },
  },
})

Vite 7.3.2 ではこの設定でビルドが通りましたが、Vite 8.0.10 に上げたあと pnpm build を実行すると次のエラーになりました。

Warning: Invalid output options (1 issue found)
- For the "manualChunks". Invalid type: Expected Function but received Object.

TypeError: manualChunks is not a function

公式の移行ガイドに記載の通り、output.manualChunks のオブジェクト形式はサポートされなくなっていました。今回の検証では、ひとまず関数形式に書き換えてビルドを通しました。

vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('typescript')) return 'vendor'
        },
      },
    },
  },
})

関数形式も将来的には非推奨とされているため、本番プロジェクトでは応急処置と捉えて、Rolldown 側の新しい分割方法への移行を前提にしておくのが良さそうです。

3. React アプリでも試す

最小構成では manualChunks しか踏まなかったので、もう少し実務に近い構成として React Router を入れた SPA も /tmp で用意しました。構成は以下の通りです。

  1. React Router でのルーティング
  2. lazy() によるページ分割
  3. .js ファイルのまま JSX を書いたレガシーコンポーネント
  4. build.rollupOptions.output.manualChunks の古い vendor 分割

この構成で Vite 7.3.2 ではビルドできましたが、Vite 8.0.10 に上げると2箇所で止まりました。

1つ目は vite.config.ts の型チェックです。TypeScript 構成が少し厳格になっているプロジェクトでは、manualChunks オブジェクト形式がビルド時ではなく型エラーとして検出されました。

Type '{ manualChunks: { react: string[]; }; }' is not assignable to type 'OutputOptions'

2つ目は .js ファイルに JSX を書いたコンポーネントの変換です。manualChunks を関数形式に直したあと、次のエラーで止まりました。

[builtin:vite-transform] Error: Unexpected token
src/legacy/LegacyStats.js:8:5

Vite 7 までは esbuild 側の挙動で .js に書かれた JSX も通っていましたが、Oxc ベースに切り替わった Vite 8 では同じコードが通らなくなっています。なお、公式の移行ガイドにはこの点の明示的な記載は見当たりませんでしたが、手元ではこの挙動になりました。今回の検証では、transformWithOxc を使う enforce: 'pre' プラグインに置き換えることでビルドが通りました。

vite.config.ts
import { defineConfig, transformWithOxc } from 'vite'

const jsxInJsPlugin = () => ({
  name: 'jsx-in-js',
  enforce: 'pre' as const,
  async transform(code: string, id: string) {
    if (!id.endsWith('/LegacyStats.js')) return null
    return transformWithOxc(code, id, { lang: 'jsx' })
  },
})

4. manualChunkscodeSplitting に置き換える

関数形式の manualChunks も非推奨になっているので、公式が推奨している output.codeSplitting を使った書き方でも同じ分割結果になるかを確認しました。

vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rolldownOptions: {
      output: {
        codeSplitting: {
          groups: [
            {
              name: 'vendor',
              test: /typescript/,
            },
          ],
        },
      },
    },
  },
})

この設定で Vite 8.0.10 でビルドすると、manualChunks 関数形式と同じ vendor-*.js チャンクが生成されました。codeSplitting.groups 配下で nametest(正規表現)を指定する形なので、manualChunks(id) で条件分岐していた処理はこちらへの書き換えがやりやすい印象です。

5. rollupOptionsrolldownOptions にリネームする

build.rollupOptionsbuild.rolldownOptions に機械的にリネームして、動作に差が出るかも確認しました。

vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    rolldownOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('typescript')) return 'vendor'
        },
      },
    },
  },
})

ビルド成果物は rollupOptions のままだった場合とバイト単位で一致しました。型定義を node_modules/vite/dist/node/index.d.ts で確認したところ、rollupOptionsrolldownOptions と同じ RolldownOptions 型のエイリアスでした。@deprecated Use 'rolldownOptions' instead. の JSDoc コメントも付いています。deprecated に対応した IDE であれば、rollupOptions と書いた時点で打ち消し線で表示されます。

6. transformWithEsbuild プラグインを動かす

transformWithEsbuild を呼び出す自作プラグインを Vite 8 で動かしたらどうなるかも確認しておきました。

vite.config.ts
import { defineConfig, transformWithEsbuild, type Plugin } from 'vite'

const esbuildJsxInJsPlugin = (): Plugin => ({
  name: 'esbuild-jsx-in-js',
  enforce: 'pre',
  async transform(code, id) {
    if (!id.endsWith('.probe.js')) return null
    const result = await transformWithEsbuild(code, id, { loader: 'jsx' })
    return { code: result.code, map: result.map }
  },
})

export default defineConfig({
  plugins: [esbuildJsxInJsPlugin()],
})

ビルドは通りましたが、実行時に次の警告が出ました。

`transformWithEsbuild` is deprecated and will be removed in the future. Please migrate to `transformWithOxc`.

transformWithEsbuild 自体は Vite 8.0.10 でも引き続き利用可能ですが、deprecation 警告付きで、将来のバージョンで削除される予定です。自作プラグインや社内共通プラグインで使っている場合は、transformWithOxc への書き換えを計画しておいた方が無難そうです。

なお、transformWithOxc に置き換える際、JSX のデフォルトランタイムが異なる点には注意が必要でした。transformWithEsbuildloader: 'jsx' を渡した場合は classic ランタイム(React.createElement)に変換されます。一方、transformWithOxc は automatic ランタイム(react/jsx-runtime への import を挿入)がデフォルトです。React が依存関係に入っていないプロジェクトで機械的に置き換えると、Rolldown failed to resolve import "react/jsx-runtime" のようなエラーになり得ます。ランタイム指定のオプションも見直しておくのが良さそうです。

検証結果の考察

manualChunks の置き換えは codeSplitting が現実的

関数形式への書き換えで一旦ビルドは通せましたが、関数形式自体も非推奨化されています。恒久対応まで考えるなら output.codeSplitting への置き換えまで含めた方が効率的そうです。今回の検証で、関数形式で書いていた分割を codeSplitting.groups に置き換えて同じ結果になることが確認できたので、既存の条件を groupstest に寄せるところから始められそうです。

Oxc への切り替えは既存の esbuild 依存で影響が出る

JavaScript の変換が Oxc に切り替わった影響は、単純なテンプレートでは表面化しませんでした。一方で、以下のような既存コードではそれぞれ対応が必要でした。

  • .js ファイルにそのまま書かれた JSX: transformWithOxc を使うプラグインで差し替え
  • transformWithEsbuild を呼び出す自作プラグイン: Vite 8.0.10 でも動作するが deprecation 警告が出る

transformWithEsbuild から transformWithOxc への置き換えは、API の引数名(loader: 'jsx'lang: 'jsx')だけでなく、JSX のデフォルトランタイムが classic から automatic に変わる点の確認もあわせて必要そうです。

rolldownOptions へのリネーム自体は無害

build.rollupOptionsbuild.rolldownOptions は Vite 8 で同じ RolldownOptions 型のエイリアスとして定義されており、rollupOptions 側に @deprecated が付いている状態でした。機械的にリネームしてもビルド成果物はバイト単位で同一で、挙動の差は確認できませんでした。

移行作業の順序としては、挙動が変わる項目(manualChunks の書き換え、transformWithEsbuild の置き換え)を優先し、rolldownOptions へのリネームはリファクタとしてまとめて入れる形が現実的そうです。

試してみた感想

素のテンプレートでは問題なくビルドが通ったのに対して、vite.config.ts に古い書き方を入れた途端にエラーや型不整合に当たるパターンが多く、Vite 本体の更新というよりは既存設定の棚卸しが中心の作業になりそうだと感じました。

また、非推奨化された API は型定義やランタイム警告で気づきやすい形になっていたので、一気に全部直すより、警告を潰しながら段階的に移行していく方が進めやすそうな手触りでした。

まとめ

Vite 7.3.2 から Vite 8.0.10 への移行を試した結果をまとめると、以下の通りです。

  • 素の Vite 7 テンプレートは Vite 8 にそのまま上がった
  • manualChunks オブジェクト形式はビルドエラーまたは型エラーとして検出された(関数形式で一旦回避、恒久対応は codeSplitting.groups への置き換え)
  • .js ファイルに JSX を書いているコードは transformWithOxc を使うプラグインへの差し替えが必要だった
  • transformWithEsbuild は Vite 8.0.10 でも動作するが deprecation 警告が出る(transformWithOxc への置き換え時は JSX ランタイムのデフォルト差にも注意)
  • rollupOptionsrolldownOptions のエイリアスとして残っており、機械的なリネームで生成物は変わらなかった

冒頭で気になっていた「どこで引っかかるか」は、Vite 本体そのものよりも vite.config.ts に残っていた古い書き方に集中していました。依存を上げる前に設定ファイルを棚卸ししておくと、移行時の切り分けがスムーズになりそうです。

誰かの参考になれば幸いです。


参考リンク:

この記事をシェアする

関連記事