react-router-dom vs wouter vs @reach/router vs react-router-native
React アプリケーション向けルーティングライブラリの選択
react-router-domwouter@reach/routerreact-router-native類似パッケージ:
React アプリケーション向けルーティングライブラリの選択

@reach/routerreact-router-domreact-router-nativewouter はすべて React 向けのルーティングライブラリですが、それぞれ異なる設計哲学と適用範囲を持っています。react-router-dom は Web 向けのフル機能ルーターとして広く使われており、react-router-native はその React Native 版です。wouter は軽量でシンプルな API を特徴とする代替選択肢です。一方、@reach/router は非推奨となっており、新規プロジェクトでの使用は推奨されません。これらのライブラリは、ルート定義、ナビゲーション、データ読み込み、プラットフォーム対応などの面で大きく異なります。

npmのダウンロードトレンド
3 年
GitHub Starsランキング
統計詳細
パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
react-router-dom17,665,95256,1065.46 kB1404日前MIT
wouter889,6747,70174.8 kB2424日前Unlicense
@reach/router387,7846,863-1736年前MIT
react-router-native19,07656,10639.9 kB14010ヶ月前MIT

React ルーティングライブラリの技術的比較:@reach/router、react-router-dom、react-router-native、wouter

React アプリケーションでルーティングを実装する際、開発者は複数の選択肢に直面します。ここでは、@reach/routerreact-router-domreact-router-nativewouter の4つのパッケージについて、実際の開発観点から深く比較します。それぞれの設計思想、API の使い勝手、制限事項、そして現状のメンテナンス状況を踏まえ、プロジェクトに最適な選択を支援します。

⚠️ @reach/router は非推奨です

まず重要な前提として、@reach/router公式に非推奨(deprecated) とされています。npm ページおよび GitHub リポジトリには明確に「このパッケージはメンテナンスされておらず、新規プロジェクトでの使用は推奨されない」と記載されています。代わりに react-router-dom v6 以降の使用が強く推奨されています。そのため、以下では歴史的文脈や移行の参考のために言及しますが、新規プロジェクトで @reach/router を選ぶべきではありません

🌐 ルート定義とマッチング:宣言的 vs 関数型

react-router-dom

react-router-dom は、v6 以降で <Routes><Route> コンポーネントによる宣言的なルート定義を採用しています。パスパラメータは :id 形式で定義し、useParams() で取得します。

// react-router-dom
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';

function User() {
  const { id } = useParams();
  return <div>User ID: {id}</div>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/users/:id" element={<User />} />
      </Routes>
    </BrowserRouter>
  );
}

wouter

wouter は軽量かつシンプルな API を特徴とし、カスタムフック中心の設計です。ルート定義は <Route> コンポーネントではなく、useRoute() フックや <Switch> コンポーネントで行います。パスパラメータは配列分割で取得します。

// wouter
import { Switch, Route, useRoute } from 'wouter';

function User() {
  const [match] = useRoute('/users/:id');
  if (!match) return null;
  return <div>User ID: {match.params.id}</div>;
}

function App() {
  return (
    <Switch>
      <Route path="/users/:id" component={User} />
    </Switch>
  );
}

react-router-native

react-router-native は React Native 向けのルーティングライブラリで、react-router-dom とほぼ同じ API を提供しますが、ネイティブ環境向けに最適化されています。<NativeRouter> をルートプロバイダーとして使用します。

// react-router-native
import { NativeRouter, Routes, Route, useParams } from 'react-router-native';

function UserScreen() {
  const { id } = useParams();
  return <Text>User ID: {id}</Text>;
}

function App() {
  return (
    <NativeRouter>
      <Routes>
        <Route path="/users/:id" element={<UserScreen />} />
      </Routes>
    </NativeRouter>
  );
}

@reach/router(非推奨)

@reach/router は JSX 内で直接パスを指定し、子要素としてコンポーネントを渡すスタイルでした。パスパラメータは props 経由で自動注入されました。

// @reach/router (非推奨)
import { Router, Link } from '@reach/router';

function User({ id }) {
  return <div>User ID: {id}</div>;
}

function App() {
  return (
    <Router>
      <User path="/users/:id" />
    </Router>
  );
}

🔁 ナビゲーションとリンク処理

react-router-dom

<Link> コンポーネントを使用して、ブラウザ履歴を操作せずにナビゲーションを行います。プログラムによるナビゲーションには useNavigate() フックを使います。

import { Link, useNavigate } from 'react-router-dom';

function Nav() {
  const navigate = useNavigate();
  return (
    <>
      <Link to="/profile">Profile</Link>
      <button onClick={() => navigate('/dashboard')}>Go to Dashboard</button>
    </>
  );
}

wouter

<Link> コンポーネントも提供しますが、より軽量な実装です。プログラムナビゲーションには useLocation() フックの setter を使います。

import { Link, useLocation } from 'wouter';

function Nav() {
  const [, setLocation] = useLocation();
  return (
    <>
      <Link href="/profile">Profile</Link>
      <button onClick={() => setLocation('/dashboard')}>Go to Dashboard</button>
    </>
  );
}

react-router-native

<Link> は React Native の TouchableOpacityText と組み合わせて使用され、タッチ操作に対応します。

import { Link } from 'react-router-native';
import { TouchableOpacity, Text } from 'react-native';

function NavButton() {
  return (
    <Link to="/profile" component={TouchableOpacity}>
      <Text>Profile</Text>
    </Link>
  );
}

📱 プラットフォーム対応:Web vs Native

  • react-router-dom: Web ブラウザ向け。HTML5 History API を利用。
  • react-router-native: React Native 向け。ネイティブのナビゲーションスタックと連携。
  • wouter: Web 専用。軽量でバンドルサイズを極力抑えたい Web アプリ向け。
  • @reach/router: Web 専用だが、非推奨のため使用不可。

重要なのは、Web と Native で同じルーティングロジックを共有したい場合でも、react-router-domreact-router-native は別々のパッケージとしてインストール・使用する必要がある点です。共通のロジックはビジネスロジック層に分離し、ルート定義自体はプラットフォームごとに記述するのが一般的です。

🧩 動的ルートとネスト構造

react-router-dom

v6 では、ルートのネストを <Route> の入れ子で表現できます。これにより、レイアウトコンポーネントとの組み合わせが容易になります。

<Routes>
  <Route path="/dashboard" element={<DashboardLayout />}>
    <Route index element={<Overview />} />
    <Route path="settings" element={<Settings />} />
  </Route>
</Routes>

wouter

ネストされたルートを直接サポートしていません。代わりに、複数の useRoute() を組み合わせるか、自前でロジックを実装する必要があります。

function Dashboard() {
  const [match1] = useRoute('/dashboard/*');
  const [match2] = useRoute('/dashboard/settings');
  // 手動でマッチングを組み立てる必要あり
}

react-router-native

react-router-dom と同様にネストをサポートしますが、React Native の画面遷移パターン(スタック、タブなど)と組み合わせる際には注意が必要です。

🔄 データ読み込みとルートガード

react-router-dom v6.4 以降では、LoaderAction という新しいデータ読み込みパターンを導入しています。これにより、ルート単位で非同期データを安全に読み込むことが可能になりました。

// react-router-dom (v6.4+)
import { json } from 'react-router-dom';

const userLoader = async ({ params }) => {
  const user = await fetchUser(params.id);
  return json(user);
};

<Route path="/users/:id" loader={userLoader} element={<User />} />

一方、wouterreact-router-native にはこのような組み込みのデータ読み込み機構はありません。代わりに、useEffect やカスタムフック内でデータをフェッチする必要があります。

// wouter
function User() {
  const [match] = useRoute('/users/:id');
  const [user, setUser] = useState(null);

  useEffect(() => {
    if (match) {
      fetchUser(match.params.id).then(setUser);
    }
  }, [match]);

  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

📦 依存関係と拡張性

  • react-router-dom: 大規模アプリケーション向けの豊富な機能(ネストルート、Loader/Action、エラーバウンダリ統合など)を提供。ただし、機能が多いため学習コストやバンドルサイズが増える可能性があります。
  • wouter: 最小限の機能に絞り、バンドルサイズを極力小さく保つことを目的としています。高度なルーティング機能が必要ないシンプルなアプリに最適です。
  • react-router-native: React Native 専用の抽象化を提供しますが、基本的なルーティング機能に留まります。複雑なナビゲーションには React Navigation と併用されることが多いです。

✅ まとめ:各パッケージの適用シナリオ

パッケージ推奨用途注意点
react-router-dom中〜大規模な Web アプリ。SSR/SSG 対応、ネストルート、データ読み込み機構が必要な場合機能が豊富な分、初期学習コストあり
react-router-nativeReact Native アプリで、React Router の API に慣れているチーム複雑なネイティブナビゲーションには React Navigation が主流
wouter軽量さを重視する小規模 Web アプリやマイクロフロントエンドネストルートや高度な機能は自前実装が必要
@reach/router使用禁止非推奨。既存コードは react-router-dom v6+ へ移行推奨

💡 最終的なアドバイス

  • 新規 Web プロジェクトreact-router-dom(v6.4+)を標準選択肢とし、必要に応じて wouter を検討。
  • React Native プロジェクトreact-router-native よりも React Navigation を第一選択肢とすることが多いが、シンプルなユースケースや既存の React Router 知識を活かしたい場合は有効。
  • 既存の @reach/router 使用プロジェクト → 速やかに react-router-dom v6 以降への移行を計画してください。

ルーティングはアプリケーションの骨格となる部分です。プロジェクトの規模、チームのスキルセット、将来の拡張性を考慮して、慎重に選択しましょう。

選び方: react-router-dom vs wouter vs @reach/router vs react-router-native
  • react-router-dom:

    react-router-dom は中〜大規模な Web アプリケーションに最適です。ネストされたルート、Loader/Action によるデータ読み込み、エラーハンドリング、SSR/SSG 対応など、本格的なアプリケーション開発に必要な機能が揃っています。チームが長期的に保守・拡張するプロジェクトでは、この安定したエコシステムを選ぶべきです。

  • wouter:

    バンドルサイズを極限まで削減したい小規模な Web アプリや、静的サイト、マイクロフロントエンドの一部として使う場合に適しています。API は最小限で直感的ですが、ネストルートや高度なデータ読み込み機構は提供されていません。シンプルさと軽量さを最優先するプロジェクトで選んでください。

  • @reach/router:

    このパッケージは公式に非推奨とされており、新規プロジェクトでの使用は絶対に避けてください。既存のコードベースがある場合は、react-router-dom v6 以降への移行を早急に計画すべきです。非推奨であるため、セキュリティ修正や新機能の追加は行われません。

  • react-router-native:

    React Native アプリで、かつ React Router の API にすでに慣れているチーム向けです。ただし、React Native の複雑なナビゲーション(タブ、スタック、モーダルなど)を実装するには、React Navigation の方がより適している場合が多いです。シンプルなルーティング要件で、Web 版とのコード共有を重視する場合に検討してください。

react-router-dom のREADME

This package simply re-exports everything from react-router to smooth the upgrade path for v6 applications. Once upgraded you can change all of your imports and remove it from your dependencies:

-import { Routes } from "react-router-dom"
+import { Routes } from "react-router"