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).
3
Answers
In your example, you have defined MyComponent as a layout route by omitting the path prop, but you still need to specify a route for it to handle nested routes. If you want MyComponent to handle the nested route /def, you need to specify that route within MyComponent. Here’s how you can modify your code to make it work:
In this modified code, MyComponent is still defined as a layout route without a path, but within MyComponent, there’s a nested Route component that handles the /def route. Now navigating to /def should display the content defined in the
<p>def</p>
element.When you omit the
path
attribute for a<Route>
in React Router v6, you’re indeed turning it into a "layout route". This means the route will act as a container for nested routes, participating in the UI hierarchy without contributing any path segment to the URL. However, for a nested route to be accessible, its parent route (the layout route, in this case) must be matched first. Your configuration misses this because you’ve placed<MyComponent />
as a direct child of<Routes>
without a path, meaning it’s not directly accessible via the URL structure.Here’s why navigating to
/def
doesn’t work as expected in your setup:/def
against your route configuration./def
at the top level (<Route path='/abc' element={<p>abc</p>} />
is the only route with a path, and it doesn’t match).<Route element={<MyComponent />}></Route>
does not specify a path, it’s considered a layout route but has no opportunity to match because it’s not nested within another route—it’s at the top level.The solution involves structuring your routes to use nesting properly. If you want
MyComponent
to serve as a layout for nested routes, you must ensure it’s accessible through some path. Addingpath='*'
makesMyComponent
match any path not matched by other routes, effectively serving as a fallback route. However, this might not be what you want ifMyComponent
is supposed to be a specific part of your application rather than a catch-all.A better approach could be to explicitly define a path for
MyComponent
as a layout route or to nest your routes in a way that aligns with your application’s structure. For example:This configuration makes
MyComponent
accessible through any path (due topath="/*"
), withdef
being a nested route within it. Note that usingpath="/*"
at the top level makesMyComponent
act as a catch-all container, which might not be ideal depending on your app’s structure. It’s usually better to define more specific paths for your layout routes.Issue
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.
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.
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.