Home > Mobile >  React router v6: catch all "*" path does not work when using nested routes
React router v6: catch all "*" path does not work when using nested routes

Time:12-02

I have the following two files

AppRoutes.tsx

import { Route, Routes } from "react-router-dom";
import NotFound from "../pages/NotFound";
import MessageRoutes from "../features/messages/routes/MessageRoutes";
import Home from "../pages/Home";

export default function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages/*" element={<MessageRoutes />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}

MessageRoutes.tsx

import { Route, Routes } from "react-router-dom";
import ProtectedRoutes from "../../../routes/ProtectedRoutes";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";

export default function MessageRoutes() {
  return (
    <Routes>
      <Route element={<ProtectedRoutes />}>
        <Route path="/" element={<MessageOverview />} />
        <Route path="/new" element={<NewMessage />} />
      </Route>
    </Routes>
  );
}

Because I'm using the path "/messages/*" to capture all paths that start with /messages, my MessageRoutes component takes care of these nested routes. I have a final "*" route in the AppRoutes component to capture any url that the app does not support. But if the path would be "/messages/loremipsum", react router does not catch the NotFound route because everything that starts with "/messages" will be handled with the MessageRoutes component.

Does this mean that in every nested route component I now have to add a final <Route path="\*" element={\<NotFound /\>} /\> again, just to support a final catch all route? I don't like this approach. Is there no absolute final catch all for every route?

CodePudding user response:

Does this mean that in every nested route component I now have to add a final <Route path="\*" element={<NotFound />} \> again

Yes, absolutely. Each Routes component manages its own "scope" of routes for what it can match. For example if the current URL path is "/messages/loremipsum" the root Routes component matches the "/messages/*" and correctly renders the MessageRoutes component. The MessageRoutes component's Routes component then works on matching on the next path segment. Since there is no "*/loremipsum" route path you need another "catch-all" route to handle this.

The issue is that a Routes component isn't aware of what descendent routes any of its routes may possibly be rendering.

Example:

import { Route, Routes } from "react-router-dom";
import NotFound from "../pages/NotFound";
import MessageRoutes from "../features/messages/routes/MessageRoutes";
import Home from "../pages/Home";

export default function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/messages/*" element={<MessageRoutes />} />
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}
import { Route, Routes } from "react-router-dom";
import ProtectedRoutes from "../../../routes/ProtectedRoutes";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
import NotFound from "../pages/NotFound";

export default function MessageRoutes() {
  return (
    <Routes>
      <Route element={<ProtectedRoutes />}>
        <Route path="/" element={<MessageOverview />} />
        <Route path="/new" element={<NewMessage />} />
        <Route path="*" element={<NotFound />} />
      </Route>
    </Routes>
  );
}

If you want to have a single "catch-all" route then you'll need to have a single routes configuration.

Example:

import { Route, Routes } from "react-router-dom";
import MessageOverview from "../pages/MessageOverview";
import NewMessage from "../pages/NewMessage";
import NotFound from "../pages/NotFound";
import Home from "../pages/Home";

export default function AppRoutes() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route element={<ProtectedRoutes />}>
        <Route path="/messages">
          <Route index element={<MessageOverview />} />
          <Route path="new" element={<NewMessage />} />
        </Route>
      </Route>
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
}

Now when/if the URL path is "/messages/loremipsum" then this Routes component knows what nested routes it is rendering and can match and can correctly render NotFound.

  • Related