Vue.js から React への移行を考えているあなたに
2025-12-06
azblob://2025/12/04/eyecatch/2025-12-06-migrating-from-vue-to-react-000_0.png

はじめに

私はこれまで、主に Vue.js や Nuxt.js を使ってアプリ開発を行ってきました。

そんな中、あるプロジェクトで Next.js(React)への移行 を行うことになりました。

Vue も React もどちらも「コンポーネントベースのフレームワーク」なので、

そこまで苦労しないだろうと正直、軽く考えていました。

……が、実際に移行を進めていくと、世界観そのものが違う ことを思い知らされました

この記事は、そのときに感じた 「移行して初めて分かったリアル」 を、できるだけ誠実にまとめたものです。

移行前に知っていれば避けられた苦労や、逆に React / Next.js に移って初めて分かった「心地よさ」も含めて共有したいと思います。

※本記事は、Next.js の App Router へ移行した際の体験談を中心に書いています。

対象読者

この記事は次のような人に向けて書いています。

個人開発者・現場エンジニア

  • Vue.js / Nuxt.js を中心に開発してきた人
  • React が気になっている人
  • Next.js への移行を検討している人
  • 「Vue の知識って、どこまで React に通用するの?」と不安に感じている人

チーム・プロジェクトの技術選定担当者

  • Vue.js / React のどちらを選ぶべきか判断したい
  • エコシステムの違いを 実務目線で 把握したい
  • 「移行コスト」がどのくらいかかるのか、ざっくりイメージしたい

背景

私はあるプロジェクトにアサインされました。

そこではすでに Nuxt.js(Vuetify, Pinia 等のパッケージ)を使ってアプリが動いており、

 ちょうど 新サービスの追加・大幅な改善 を検討しているタイミングでした。

これに合わせて、一度技術選定を見直そうという話が出たのが、移行プロジェクトの第一歩でした。

デジタル庁のデザインシステムを使うことに

新しく進める機能開発では、次のような方針が掲げられました。

デジタル庁デザインシステム

調査を進めていく中で分かったのが、

デジタル庁のデザインシステムの コードスニペットが React で公開されている

digital-go-jp/design-system-example-components-react: デジタル庁デザインシステムのサンプルコンポーネント(React版)

という事実でした。

つまり、React 版のコンポーネントをそのまま活用できれば、

  • 再実装コストの削減
  • 品質・アクセシビリティ水準の担保
  • 継続的なデザイン統一

といったメリットが得られます。

Vue.js / Nuxt.js で同じものをゼロから作るよりも、

 React ならデジタル庁が提供しているサンプルを使用できるのが非常に魅力的でした。

生成AIを活用した開発

今回のプロジェクトでは、開発効率を高めるために、エージェント型コーディングツールである「Claude Code」を積極活用する方針も含まれていました。

Claude Code | Claude

Claude Code

実際に AI に React のコードを生成させてみると、Vue と比べて次のような違いを感じました。

  • 生成されるコード量が多い(サンプルがリッチ)
  • 「とりあえず動く」コードが出てくることが多い
  • 周辺ライブラリまで理解している
  • サンプルコードやデバッグパターンの提案精度が高い

これは一言でいうと、「AI が React の世界観に最適化されている」という印象でした。

Vue.jsも、もちろん悪くはないのですが、

  • Vue 2 → Vue 3 のような 「文法・書き方・思想そのものが変わるようなレベルの大規模変更」があったこと
  • AI が古い Vue 2 の情報を参照していると、Vue 3 のアプリでは動かないコードが出てくること

などの影響で、「そのままでは動かないコード」が混ざるケースも少なくありませんでした。

また、検索数という観点でReactとVue.jsを比較してみると、

世界でも、日本でもReactのほうが常に多いことがわかります。

React, Vue - 調べる - Google トレンド

検索数

検索されているということは、

  • エンジニアが採用を検討したり
  • トラブルシュートのために調べたり

といったタイミングで React がより多く参照されており、React の資料・コミュニティが大きいため、コード生成の事例が豊富であること推測できます。

エコシステムと対応ライブラリの数

React のエコシステムはとにかく巨大です。

たとえば、

  • UI ライブラリ
  • 状態管理
  • フォームライブラリ
  • SSR / SSG フレームワーク
  • アニメーション
  • A11y ツール
  • テストツール
  • AI / LLM 関連ツール

など、ほぼすべてのカテゴリで React 系ライブラリのほうが量も選択肢も多い という特徴があります。

Vue はどちらかというと 統合的・まとまりが良い エコシステムで、 そのぶん特定領域を深くカバーしてくれる強みがあります。

ただしAI が理解する前提のデータ量という観点だと、React のほうが圧倒的に有利だと感じました。

総合的に判断して React(Next.js)を採用

こうした背景を踏まえて、次期機能追加では React(Next.js)を採用する理由 がかなり明確になりました。

  • デジタル庁デザインシステムが React 前提で提供されている
  • A11y(アクセシビリティ)対応済みの公式コンポーネントをそのまま活用できる
  • AI エージェント(Claude Code)を用いた開発では、React のほうが生成精度・開発効率が高い
  • Vue / Nuxt で同じ品質と速度を再現しようとすると、再実装コストが大きくなる

これらを踏まえると、

部分的に React を導入するだけでなく、アプリ全体を React(Next.js)へ統一した方が合理的では?

という結論に至りました。

こうして、最終的に Vue.js / Nuxt.js から React / Next.js へ移行する ことが決定しました。

実際に移行してつまづいたこと

JSX 構文への戸惑い

まず最初にぶつかった壁は、やはり JSX 構文 でした。

React では JavaScript 内で UI を記述する JSX を使ってコンポーネントを作ります。

これに慣れるまで、かなり頭の切り替えが必要でした。

「ボタンをクリックしたらログが出る」程度のシンプルなコードで比較してみます。

Vue.js

<template>
  <button @click="sayHello">クリック</button>
</template>

<script setup>
const sayHello = () => {
  console.log('Hello World')
}
</script>

Vue.js(Composition API)では、HTML の延長のような書き方 ができます。

  • ロジックは <script>
  • 表示は <template>

と明確に分離されているので、学習コストが低いとよく言われます。

React

export default function App() {
  const sayHello = () => {
    console.log('Hello World')
  }

  return (
    <button onClick={sayHello}>クリック</button>
  )
}

React(JSX)では、JavaScript の関数としてコードを書き、その戻り値として UI 要素を返す 形になります。

ロジックと表示が同じ場所に存在しているのが、Vue との大きな違いです。

JSX のメリットが腑に落ちるまで

正直なところ、移行したての頃は JSX のコードを見ても、

「どこが UI で、どこがロジックで、どこがイベントで、どこが状態管理なのか」

その境界が曖昧で、かなり混沌としたもの に見えていました。

しかし、実務で React / Next.js を使い続けていくと、次のようなメリットを実感するようになりました。

自由度が高い

Vue.js には、フレームワーク独自の構文が多くあります。

  • v-if
  • v-show
  • v-for
  • v-slot
  • v-model

など、これらはとても分かりやすい反面、「Vue のやり方」に沿う必要があるとも言えます。

一方、React では UI にそのまま JavaScript を使える のが特徴です。

{items
  .filter((x) => x.done)
  .map((x) => (
    <Task key={x.id} {...x} />
  ))}

このような JS の関数(filter / map など)を使ったロジックを、そのまま UI と同じ場所に書ける のは、慣れてくるとかなり快適です。

責務が明確になりやすい

Vue.js は template / script / style とファイル内で役割が分かれていて見やすい一方、

画面が複雑になってくると、

  • UI
  • 状態
  • 計算プロパティ
  • イベント
  • メソッド

がファイルの離れた場所に散らばりがちです。

「この処理どうなってるんだっけ?」と追いかけるたびに、

画面を上下にスクロールしまくった経験がある人も多いと思います。

React では、関数コンポーネント(UI を返す関数) の中に必要な情報が集約されるため、

  • この state はこの UI でしか使わない
  • この関数はこのコンポーネントの中だけで完結している
  • この分岐はこのレンダリングにしか影響しない

といった関係性が、物理的に近い場所にまとまる 傾向があります。

Vue 脳のままでは最初はピンと来ないのですが、 数週間ほどガッツリ React に触れて「体験として」理解してくると、「なるほど、こういう思想で作られた書き方なのか」と腑に落ちてきました。

(これが分かってくるのは、ある程度数をこなしてからなんですが……笑)

Server Component と Client Component の衝撃

Next.js(App Router)で最初にぶつかった大きな壁が、Server Component / Client Component の概念でした。クライアント・サーバーの概念はわかっていても、これらを理解するのには時間がかかりました。

Nuxt.jsではコンポーネントはクライアントで動作する前提で開発されます。

SSR(サーバーサイドレンダリング)したとしても、最終的ににはクライアント側でハイドレートされます。

そのため、onClickv-modelは基本的にどこに書いてもOKです。

しかし、この考え方はNext.jsでは通用しません。

Next.js(App Router)では、コンポーネントに役割が割り振られています。

それが「Server Component」と「Client Component」です。

飲食店で例えると、

  • Server Component:料理を作るキッチン担当
  • Client Component:料理をお客さんに渡したり、注文を受けるホール担当

みたいな感じです。

もっと具体的に言うと、以下の役割を負います。

  • Server Component:データ取得、認証、DB操作、cookies、RSCなど
  • Client Component:入力、DOM操作、アニメーション、画面操作など
Client ComponentとServerComonentの比較

これらの概念がVue.jsにはなかったので、思想の違いがダイレクトに実感できるつまづきポイントでした。

※Next.js には、クライアントコンポーネントからサーバー関数を呼び出せる Server Action という機能もあります。ただし今回は、コンポーネントによって役割が明確に分かれるという Next.js の基本思想を中心に説明するため、Server Action については説明していません。

コード例

以下のようなコードを書くと、Next.js ではエラーになります。

Javascriptexport default function Page() {
  const handleClick = () => {
    console.log('clicked')
  }

  return <button onClick={handleClick}>クリック</button>
}

このコンポーネントはServer ComponentなのでonClickが使えません

Client Componentにしてください

これを動かすにはこうします。

Javascript"use client" // ☆追加!

export default function Page() {
  const handleClick = () => {
    console.log('clicked')
  }

  return <button onClick={handleClick}>クリック</button>
}

はい、一行目に”use client”と記載しただけです。

これでこのコンポーネントはClient Componentと認識され、エラーがなくなります。

そして、ここで出てくる発想が、

「これもう全部に”use client”つければよくない?」

思いついた人

画面でonClickを使うたびにエラーになるなら、

最初から全部Client Componentにすればいいじゃん!!

私も最初はこれで書き始めました。

実際、"use client"をつければ、

  • useStateを使える
  • useEffectを使える
  • onClickもonChangeも使える

と、Vue.jsのコンポーネントに近い感覚で実装ができます。

そのため、一瞬だけ世界が平和になります。(しかしこの考えは間違いでした...「認証とセキュリティ」に続きます)

ライブラリ互換がなく、UI・ロジックを作り直し

Server / Client Component の考え方に慣れてきたころ、次に直面したのが ライブラリと実装方針の違いによる壁 でした。

Vue のライブラリは React では使えない

…当たり前なんですが、問題はその先です。

React のライブラリは膨大で、「定番」 が Vue.js ほど明確ではありません。

React で悩むポイントの例

  • UI:MUI / Chakra / shadcn / Radix / Mantine …
  • 状態管理:Zustand / Redux / Jotai / Recoil …
  • フォーム:react-hook-form / Formik
  • Fetch:SWR / React Query
  • CSS:Tailwind / styled-components / emotion / vanilla-extract

Vue はある程度絞られているので 選択のストレスが少ない のですが、React はそうはいきません。

そして「ライブラリを決めれば終わり」ではありません。

Nuxt の魔法は Next では通用しない

Vue.jsのロジックをReactにそのまま流用してもほぼ確実に動きません。

動くとしてもその実装はベストプラクティスとは言えない場合が多いです。

先述したとおり、Vue.js/Nuxt.js は基本クライアントで動く前提で、

  • フォーム入力
  • データの保持
  • API 取得
  • UI の切り替え
  • state の共有

などを 1つのコンポーネントの中で なんとなく書いても動く設計になっています。
これは良い意味での魔法です。

しかし Next.js(App Router)は設計思想が真逆です。

Vue.js で動いていたロジックをそのまま React に持っていこうとすると、

  • Server / Client コンポーネント分割
  • 認証ロジックの移管
  • データ取得の場所の見直し
  • 状態管理の責務の再定義
  • fetch のキャッシュ挙動の理解
  • Server Actions 移行 etc...

と、ほぼ別物のアーキテクチャ設計が必要になります。

Vue.js の頃は UI とロジックを同じ場所でなんとなく書いても成立したけど、
Next.js では「これはサーバー」「これはクライアント」と役割をはっきり分けないと壊れます。

ライブラリの選定だけでなく、
ロジックの構造そのものを一度更地にして組み直す必要があります。

これが想像以上に重くて、正直言うと一番しんどかったポイントでした。

認証とセキュリティ

全部Client Componentで書き始めてしばらくすると、次にやってくるのが「認証どうする問題」です。

Nuxt.js の感覚だとこのように考えがちです。

  1. トークンを localStorage に入れておく
  2. ページ側の onMounted / useAsyncData 的なところで API を叩いてユーザー情報を取る
  3. それをグローバル state(≒ストア) に入れて各画面で参照する

これを Next.js でもそのままやろうとすると、

  • 認証トークンがクライアントにべったり残り続ける
  • XSS や盗み見のリスクが上がる
  • API 叩くたびにフロント側でトークンを付与する必要がある

という セキュリティ的にけっこう危ない構成 になってしまいます。

Next.jsでは「認証回りはサーバーで完結させるべき」という思想があります。

クッキーでセッションを持って、サーバー側で認証状態を判断する考え方で、

  • クッキーでセッションを安全に管理
  • 認証判定も Server Component 側で行う
  • cookies()getServerSession() は Server 側限定

そのため、全部 Client Component だけで完結しようとすると、

Next.js が提供するサーバー側で安全に認証を扱う仕組みを十分に活かせなくなります。

ここでようやく理解しました。

「あ、これ全部 Client にすれば解決っていう話じゃないんだ」
「認証とか重要なロジックは Server に寄せないと危ないんだ」

Nuxt.js から移行してきた自分にとって、この思想の転換が本当に大きな壁でした。

Nuxtでやっていた一般的なセキュリティ設計に加えて、Next.jsではこのような思想の違いがあるので注意が必要だと感じました。

そもそもレビューできる人がいない

移行プロジェクトの初期で、実はかなり深刻だったのが 

「React / Next.js のコードをレビューできる人が少ない」 という問題でした。

元々の技術スタックが Vue.js / Nuxt.js である以上、チーム全体の知識も Vue.js 側に寄っています。
そのため、Claude Code が生成してくる React / Next.js のコードに対して、

「これって本当にベストプラクティスなの?」「動くけど、この書き方で問題ないの?」

といった判断ができませんでした。

Claude Code は React のエコシステムに最適化されているため、

  • とりあえず動くコード
  • それっぽいコード
  • 見た目は正しそうなコード

を普通に出してきます。そしてそれが正しくないケースも普通にあります。

しかし、当時の私達にはそれを見抜く知識が不足していました。

レビューが「座学」から始まった

結果として起きたのが、このレビューフローです。

  1. Claude Code を使ってPR を作る
  2. レビュワーが読む
  3. よく分からない
  4. React / Next.js のドキュメントを読む
  5. マークアップ、State、RSC、Context の概念を学ぶ
  6. 「たぶんここはこうしたほうがいい?」と推測でレビューする

つまり、レビューの前に「React/Next.jsの勉強」をする必要が出てきたのです。

Vue.js / Nuxt.js の経験はある程度役に立ちますが、思想が真逆な部分が多いため、

Vue.jsのノリで判断すると危ないという瞬間も多く、

ドキュメントや技術サイトに頼りながら進める状態でした。

この「React をレビューできない問題」は、単なる技術的つまずきではなく、

チーム全体が新しい技術基盤に移る際に避けて通れない「立ち上がりの混乱」 に近いものでした。

どうすればよかったのか

ここまで振り返ると、Vue.js / Nuxt.js から React / Next.js への移行は、想像以上に前提の違いに振り回されるプロセスだったと感じます。

ただ、後になって思えば「もっとこうしておけばスムーズだったのでは?」と思うポイントがいくつかあります。

1. React / Next.js の思想を先に学んでおくべきだった

「そりゃあそうだろ」という話ですが、チーム全員で座学から始めるべきでした。

重要なのはチーム全員が、という点です。

Vue と React はコンポーネントフレームワークという点では似ていますが、思想はまったく別物です。

  • JSX が前提
  • UI とロジックが同じ場所にある
  • Server / Client の明確な分離
  • データ取得の責務が根本的に違う

これらを知らないまま移行を始めると、必ずつまずきます。
「コードを写し替える移行」ではなく「設計思想を学び直す移行」 と最初に理解しておくべきでした。

2. 小さく実験するフェーズをもっと早く作るべきだった

実際の移行では、既存アプリのコードを触りながら React / Next.js を学んでいく形になっていました。
しかし本当は、もっと早い段階で理解のためだけに触る小さな実験環境を用意しておくべきでした。

例えば、

  • RSC のみを扱うミニアプリ
  • Server Actions の挙動だけを確認するページ
  • 認証(session / cookie)だけを試す簡易プロジェクト

こうした 目的特化のサンドボックス環境があるだけで、余計な要素に邪魔されず純粋に Next.js の特性を理解できます。

先にこうした「安全に失敗できる環境」を作っておけば、

思想の理解 → 手触りの確認 → 具体的な実装 

という自然なステップを踏めて、移行時の不安はかなり減っていたはずです。

3. ライブラリ選定を「理由付き」で行うべきだった

React のライブラリ選定は想像以上に難しく、Vue のように定番が分かりやすい世界ではありません。

選ぶときの基準を最初に明確にしておけばよかったと感じます。

  • 公式ドキュメントの充実度
  • メンテ状況
  • React の今の潮流(RSC / Server Actions)との相性
  • 学習コスト

「とりあえず有名だから」ではなく、「なぜこれを使うのか?」の理由を言語化して選ぶべきでした。

4. レビューできる体制を整える

Claude Code はとても優秀ですが、

  • 正しいけど冗長なコード
  • 動くけどベストではないコード
  • 一見正しく見えるけど思想に沿っていないコード

が普通に出てきます。

これを見抜ける人が少なかったため、レビューが勉強から始まってしまっていました。

(これは「1. React / Next.js の思想を先に学んでおくべきだった」にも繋がる話だと思っています。)

本当は、

  • チーム内で React / Next の基礎勉強会をする
  • 小さくベストプラクティスを共有する

こういった準備をしておいたほうが、移行中の不安はかなり減ったはずです。

おわりに

Vue.js / Nuxt.js から React / Next.js への移行は、単なるフレームワーク変更ではありませんでした。

移行の過程では、思想の差、設計の差、学習コスト、役割分担、レビュー体制など、想像していた以上に多くの壁が立ちはだかりました。

どれだけ優れたフレームワークでも、十分な理解なし開発すると技術の良さを引き出すことができません。

逆に、手触りを掴み、設計を理解し、チームで共通認識をつくったうえで取り組めば、React / Next.js の強みは確実に活きてきます。

現在の私たちのチームでは学びを積み重ねながら改善が進んでおり、「理解しながら進めること」がどれほど大切かをあらためて実感しています。

この記事が、これから移行を検討する人や、同じような壁に直面している人にとって、少しでも判断材料や安心材料になれば嬉しいです。