Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-router v6: route without a path

In the documentation for react-router v6 (https://reactrouter.com/en/main/route/route) it says

Omitting the path makes this route a "layout route". It participates in UI nesting, but it does not add any segments to the URL

So why doesn't this work?

const App = () =>
  <Routes>
    <Route path='/abc' element={<p>abc</p>} />
    <Route element={<MyComponent />}></Route>
  </Routes>
    
const MyComponent = () =>
  <Routes>
    <Route path='/def' element={<p>def</p>} />
  </Routes>

In this example, navigating to "/def" gives the error:

No routes matched location "/def"

If I add path='*' to the route referencing MyComponent then it works OK, but it seems the documentation is telling me I don't need to do this (and in fact the docs don't mention path='*' as far as I can see).

like image 363
Andy Avatar asked May 15 '26 05:05

Andy


1 Answers

Issue

So why doesn't this work?

const App = () =>
  <Routes>
    <Route path='/abc' element={<p>abc</p>} />
    <Route element={<MyComponent />}></Route>
  </Routes>
    
const MyComponent = () =>
  <Routes>
    <Route path='/def' element={<p>def</p>} />
  </Routes>

I think you are conflating what layout routes do with nested routes with descendent routes that any routed component can render further down the ReactTree. A pathless layout route like this doesn't participate in route matching, so it's expecting nested routes to be declared so that they can participate.

const App = () =>
  <Routes>
    <Route path='/abc' element={<p>abc</p>} />
    <Route element={<MyComponent />}>
      {/* no nested routes to match 🤷🏻‍♂️ */}
    </Route>
  </Routes>

Explanation

Layout Routes

When using layout routes you directly wrap and render nested routes. Layout routes serve the purpose of generally providing some "common UI" and an Outlet for the nested routes to render their content into.

Example using layout routes.

const App = () => (
  <Routes>
    <Route path='/abc' element={<p>abc</p>} />
    <Route element={<MyComponent />}> // <-- parent route
      <Route                          // <-- nested route
        path='def'                    // <-- "/def"
        element={<p>def</p>}          // <-- rendered into Outlet
      />
    </Route>
  </Routes>
);

const MyComponent = () => (
  <div>
    {/* Common UI header, etc */}
    <Outlet />
    {/* Common UI footer, etc */}
  </div>
);

Descendent Routes

Layout routes play a non-existent role when rendering descendent routes.

One very important distinction though is that in order for descendent routes to be reachable and matchable the parent route must append the wildcard matcher, or splat, to its path.

Descendent routes build their paths relative to their parent route. This is why your code worked when you added path="*" to the parent route. This will match anything by "/abc" which is matched by the first route.

Example using descendent routes.

const App = () => (
  <Routes>
    <Route path='/abc' element={<p>abc</p>} />
    <Route     // <-- parent route
      path="*" // <-- match anything but "/abc"
      element={<MyComponent />}
    />
  </Routes>
);

const MyComponent = () => (
  <div>
    {/* Common UI header, etc */}
    <Routes>
      <Route       // <-- descendent route
        path='def' // <-- "/" + "def" -> "/def"
        element={<p>def</p>}
      />
    </Routes>
    {/* Common UI footer, etc */}
  </div>
);
like image 167
Drew Reese Avatar answered May 16 '26 17:05

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!