react-router: [Bug]: Relative navigation issue while navigating from "index" route: ".." doesn't works, I must use "../.." to go back

What version of React Router are you using?

6.0.2

Steps to Reproduce

<Some other routes>
....
<Route path='/pages'>
    <Route index element={<Pages />} />

    <Route path=':pageId' element={<Item />} />              
  </Route>
  

Navigation via ‘…’ from Pages with “index” doesn’t works but it is ok for Item I should use ‘…/…’ for the same result as ‘…’

Expected Behavior

Should navigate level up via ‘…’ from index route And it would be nice to have option with nested routes that is used only to create url hierarchy for relative navigation usage, but without children and parrents ui rendering

Actual Behavior

doesnt navigate from index page via ‘…’

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 6
  • Comments: 21 (11 by maintainers)

Most upvoted comments

The workaround is to use resolvePath that has no relation to <Route> tree:

const navigate = useNavigate();
navigate(resolvePath('..', window.location.pathname));

// or

<Link to={resolvePath('..', window.location.pathname)} />

// or

<Navigate to={resolvePath('..', window.location.pathname)} />

I experience this problem and it makes kind of no sense how these work. I actually expect it to go up a route in the tree as opposed to just removing one segment (although in this particular instance it’s the same). Here are my routes:

<Route path="/settings">
  <Route index element={<SettingsRoute />} />
  <Route
    path="account"
    element={<SettingsRoute page="account" />}
  />
  <Route path="team">
    <Route index element={<SettingsRoute page="team" />} />
    <Route
      path="create"
      element={<CreateUserRoute />}
    />
    <Route path=":id">
      <Route index element={<EditUserRoute />} />
      <Route
        path="history"
        element={<UserActivityHistoryRoute />}
      />
    </Route>
  </Route>
</Route>

Here’s the list of routes I have with the desired to=".." behavior:

/settings -> nothing, it's the root
/settings/account -> go back to /settings, works
/settings/team -> go back to /settings, does not work since the index' parent route is path="team", which is silly
/settings/team/create -> go back to /settings/team, works
/settings/team/:id -> go back to /settings/team, does not work since the index' parent route is path=":id", again silly
/settings/team/:id/history -> go back to /settings/team/:id, works

So the silly behavior comes from how the index routes are handled - they’re treated as children of the parent route instead of being treated as the parent route itself.

Basically what I’m saying is, I think to=".." inside index routes should be treated as to="../.." for proper predictability. Otherwise, there’s no workaround for this right now since I can’t just render the element in the parent route, since that’ll render it for all child routes; and I can’t move everything a level up since that’ll break navigation for the rest of the child routes.

Upd: I realize that’s what everyone is saying, kinda got lost in the point while trying to make it. But the conclusion stands - index routes should ignore one parent in the tree when going back.

I think it is because .. means navigate one <Route> up in the routes tree and not one path segment (directory) up. Consider this example:

function NavigateUp() {
  return <Navigate to='..' />;
}

<Route path='/test'>
  <Route path=':a/:b/:c' element={<NavigateUp />} />
  <Route path=':a' element={<NavigateUp />} />
</Route>

Both /test/a/b/c and /test/a are redirected to /test because one route up in the tree is <Route path='/test'> that is really weird. I can’t find this in documentation.

This has been fixed in #8697 / #8954. Routes that do not provide any path should not contribute to relative pathing logic - should be released in 6.4.0

Update: One more case handled in #8985

Whew … this is a tough one but I get it.

The question is:

  1. do we remain consistent that .. means “up the route hierarchy”
  2. or do we add special cases for index and pathless layout routes?

I think everybody is right that (2) is probably better. So the explanation would be

".." means up the route hierarchy, ignoring routes without paths

Seems easy enough to explain and both index and pathless layout routes don’t have any paths.

So the silly behavior…

What’s the point of life if we can’t be silly sometimes?

Is there any way/workaround to receive the preivous behaviour? I’ve got a use-case where the previous behaviour was really beneficial around optional props.

In V5 we had a route with an optional parameter (options/:option?), which we now render like this:

<Route path="options">
  <Route index element={<ListPage />}/>
  <Route path=":option" element={<ListPage/>}/>
 </Route>

We were hoping that in ListPage we could define our routes with a simple <Link to="../<option id>">Option</Link>. But since index routes are ignored, the routing ends up leaving options.

This is fixed in the above mentioned PRs - should be included in the next 6.4.0 prerelease 👍

This is REALLY annoying with index routes and used to work in beta.

Imagine scenario

<Route path=":id/">
  <Route index element={<Link to="../../sibling" />} />
  ....
</Route>

Seems that a route match gets generated for both the parent route and the nested index route. Matches array of RouteContext:

(5) [{…}, {…}, {…}, {…}, {…}]
0: {params: {…}, pathname: '/', pathnameBase: '/', route: {…}}
1: {params: {…}, pathname: '/person', pathnameBase: '/person', route: {…}}
2: {params: {…}, pathname: '/person/203876', pathnameBase: '/person/203876', route: {…}}
3:
    params: {personId: '203876'}
    pathname: "/person/203876/items"
    pathnameBase: "/person/203876/items"
    route: {caseSensitive: undefined, element: {…}, index: undefined, path: 'items', children: Array(4)}
4:
    params: {personId: '203876'}
    pathname: "/person/203876/items/"
    pathnameBase: "/person/203876/items/"
    route: {caseSensitive: undefined, element: {…}, index: true, path: undefined, children: undefined}

Reproducible demo https://stackblitz.com/edit/github-1iyzlr?file=src%2FApp.tsx

Thus navigating to .. in my case, would pop the index page out of the match stack, and then take the user to that address. But that ends up being the link to the nesting “parent” page

@gowthamvbhat, thanks for pointing out that. As for me, it is better to write unlike <a href> that goes up by path segments.