React Routerの「No routes matched location」エラーを修正する方法

beginner⚛️ React2026-03-25| React 18以降、React Router v6(react-router-dom 6.x)、Node.js 18以降、OS問わず

Error Message

No routes matched location "/dashboard"
#react#react-router#ルーティング#spa

状況

/dashboard に移動する — またはそこへの <Link> をクリックする — と画面が真っ白になります。コンポーネントも、エラー境界も、何も表示されません。コンソールにはこう表示されます:

No routes matched location "/dashboard"

React Router v6 は <Routes> ブロック内のすべての <Route> をスキャンしましたが、一致するものが見つかりませんでした。よくある原因は5つあります。一つずつ確認していきましょう。

デバッグ手順

1. ルートツリーの実際の構成を確認する

ルートを定義しているコンポーネントを開き、パスを一文字ずつ読み確認してください。大文字が一つあるだけですべてが壊れます:

// 間違い — path が "Dashboard"(大文字のD)
<Route path="/Dashboard" element={<Dashboard />} />

// Link は小文字で指定している
<Link to="/dashboard">Go</Link>

React Router のパスは大文字・小文字を区別します。/Dashboard/dashboard は完全に別のルートです — 自動的に合わせてくれるようなことはありません。

2. の内側にルートがあるか確認する

<Routes> コンポーネントの外側に置かれた <Route> は、エラーも出さずに無視されます。何も起こらないだけです:

// 間違い — Route が Routes の外にある
<BrowserRouter>
  <Route path="/dashboard" element={<Dashboard />} />
</BrowserRouter>

// 正しい
<BrowserRouter>
  <Routes>
    <Route path="/dashboard" element={<Dashboard />} />
  </Routes>
</BrowserRouter>

3. ネストされたルートのパスを確認する

React Router v6 では、子ルートのパスは相対パスです。/ を先頭に付けたり、親のセグメントを繰り返したりしてはいけません:

// 間違い — 子パスが / で始まっている
<Route path="/app" element={<AppLayout />}>
  <Route path="/app/dashboard" element={<Dashboard />} />
</Route>

// 正しい — 子パスは相対パス
<Route path="/app" element={<AppLayout />}>
  <Route path="dashboard" element={<Dashboard />} />
</Route>

子ルートに path="/app/dashboard" と書くと、React Router はそれをルートからの絶対パスとして扱います — 親からの相対パスではありません。dashboard とだけ書けば、完全な URL は自動的に /app/dashboard に解決されます。

4. (または相当品)がアプリ全体をラップしているか確認する

<Routes> の上位にルーターコンテキストがなければ、何も機能しません。エントリーポイントをよく確認してください:

// main.tsx または index.tsx
import { BrowserRouter } from 'react-router-dom';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>
);

よくある落とし穴:Remix や TanStack Router を使っている場合、その上に <BrowserRouter> を追加してはいけません。これらのフレームワークは独自のルーターコンテキストを提供しています。二つのルーターコンテキストが重なると互いに干渉し、まさにこのエラーを引き起こします。

5. ルートを隠す条件付きレンダリングに注意する

これは見落としやすいパターンです。ルートの定義が、最初のレンダリング時に false になる条件の中に入っています:

// バグ: ルートがユーザー読み込み後にしかレンダリングされない
function App() {
  const { user } = useAuth();
  return (
    <Routes>
      {user && <Route path="/dashboard" element={<Dashboard />} />}
    </Routes>
  );
}

ページ読み込み時、user はまだ null です。ルートはまだ存在しません。そのため /dashboard に直接アクセスすると、user が 50ms 後に truthy になるとしても、即座に No routes matched が発生します。ルートは常に無条件で定義してください。アクセス制御はコンポーネント内またはラッパーで行いましょう:

function RequireAuth({ children }: { children: JSX.Element }) {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" replace />;
  return children;
}

function App() {
  return (
    <Routes>
      <Route
        path="/dashboard"
        element={
          <RequireAuth>
            <Dashboard />
          </RequireAuth>
        }
      />
      <Route path="/login" element={<Login />} />
    </Routes>
  );
}

完全な動作例

以下は最小構成の正しい React Router v6 アプリです — 何かおかしいときの参考としてコピーしておいてください:

// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

// App.tsx
import { Routes, Route, Navigate } from 'react-router-dom';
import Dashboard from './pages/Dashboard';
import Login from './pages/Login';
import Home from './pages/Home';

export default function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/dashboard" element={<Dashboard />} />
      <Route path="/login" element={<Login />} />
      {/* キャッチオール — 真っ白な画面の代わりに404を表示 */}
      <Route path="*" element={<div>404 Not Found</div>} />
    </Routes>
  );
}

末尾の path="*" キャッチオールはすべてのプロジェクトに追加する価値があります。真っ白な画面はデバッグの手がかりを何も与えてくれません。404 ページがあれば、少なくともルーターが動いていてパスが単に一致しないということがわかります。

修正の確認

  • ブラウザで URL に直接移動する(ハードリフレッシュ)。正しいコンポーネントがレンダリングされるはずです。
  • <Link to="/dashboard"> をクリックする — ページ全体のリロードなしに画面遷移が行われるはずです。
  • React DevTools を開き、ルーターコンテキストを見つけて、マッチしたルートが /dashboard になっているか確認する。
  • ブラウザのコンソールを確認する — No routes matched の警告が消えているはずです。

補足:ハッシュルーターとブラウザルーター

静的デプロイ(サーバーサイドルーティングなし)には別の落とし穴があります。アドレスバーに /dashboard と直接入力すると、ブラウザはそのリクエストをサーバーに送信します。サーバーは index.html しか知らないため、React が読み込まれる前に 404 を返してしまいます。

対処方法は2つあります:

  • すべてのパスを index.html にリダイレクトするようサーバーを設定する — Nginx、Apache、または Cloudflare Pages での正しい修正方法です。
  • <HashRouter> に切り替える — URL が /#/dashboard の形式になり、クライアントから外に出ることがなくなります。
// SPA 向け Nginx 設定
location / {
  try_files $uri $uri/ /index.html;
}

症状は同じように見えますが、原因は異なります。ネットワークタブを開いてください。HTML ファイルへの 200 レスポンスがあり、かつコンソールに No routes matched が表示されている場合はルーティング設定の問題です。サーバーから直接 404 が返ってくる場合はデプロイ設定の問題です。この一つの確認で、二つのケースをすぐに区別できます。

Related Error Notes