8

In React Router v6, how can I go back to the previous page using <Link> instead of useNavigate().

// Instead of this...
<button onClick={() => navigate(-1)}>
// ...it needs to be this:
const previousPage = ???

<Link to={previousPage}>Go back</Link>

Changing it to <Link> would allow me to use <a href="xxxx"> instead of a <button>, which is the most accessible way of creating links between pages. (More about button vs links). For example, it allows users to right-click and open the page in a new tab.

But I don't know what URL to pass to the Link.

Update: I've created a codesandbox to help you find the solution.

Update 2: Based on the answers and some research, I created a GitHub issue to react-router

Update 3: I've added my own answer to this question below.

sandrina-p
  • 3,794
  • 8
  • 32
  • 60

5 Answers5

6

It's not a bug in react-router-dom@6 and not really anything a Link is meant to do, i.e. it's just an anchor tag under the hood, and links to a path as a declarative navigation action. This is fundamentally different than an imperative action like history.go(1) or history.back() (i.e. history.go(-1)) where you are imperatively saying to navigate forward/backward through the history stack.

In react-router-dom@6, however, you can navigate relative to the current path. ".." will navigate "back" one path segment for any Route components rendered within the current Routes component. I.E. from "/destiny" back to "/" with the same Routes component.

Example:

import { Link } from 'react-router-dom';

export default function Destiny() {
  return (
    <div>
      <h1>Destiny Page</h1>
      <Link to={'..'}>Go back</Link>
      <br />
      <Link to="/">Go Home ("/")</Link>
    </div>
  );
}

Note that this isn't the equivalent of navigate(-1) as it isn't transitioning through the history stack, so it's not a true back navigation. If you want to do a true back navigation then you'll want to add an onClick handler to the Link and prevent the default event action and handle issuing an imperative back navigation.

Example:

import { useNavigate, Link } from 'react-router-dom';

export default function Destiny() {
  const navigate = useNavigate();

  return (
    <div>
      <h1>Destiny Page</h1>
      <Link
        to={'..'}
        onClick={(e) => {
          e.preventDefault();
          navigate(-1);
        }}
      >
        Go back (-1)
      </Link>
      <br />
      <Link to="/">Go Home ("/")</Link>
    </div>
  );
}

Edit react-router-go-back-using-link

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • 1
    Yes, `to={'..'}` is a hack. The solution with `onClick` is not great because if the user opens the link on a new page (eg right click on it), it won't go to the same route as `navigate(-1)`. I believe the solution is using vanilla JS to get the latest visited page and set it as href. I'll try to accomplish that later tomorrow. – sandrina-p Jun 21 '22 at 10:44
  • @sandrina-p Sorry, I don't follow your comment regarding the `onClick` handler not working, it uses the same `navigate(-1)` that would be used elsewhere. I saw your comment in github, can you clarify your comment about not being accessible? I can't quite wrap my head around the comment about opening a new page and getting the code to not go to the intended target, so perhaps I'm not understanding what you are specifically referring to. – Drew Reese Jun 21 '22 at 15:17
  • 1
    Here's the [Loom video](https://www.loom.com/share/d7b83c40de9d4dfa8e9592801f148125) I made showcase the problem. Try homepage > middle > destiny. Click on "go back(-1)" with a right click (or copy the link). It will to go to the "homepage" instead of the "middle" page. – sandrina-p Jun 21 '22 at 18:19
  • 1
    @sandrina-p I see, I was suspecting it was an issue with the way `".."` resolved to a target URL. Like I said, it's not technically a back navigation, it navigates a path segment back/up. There's not really any anchor-tag/link way to issue a back navigation other than providing an `onClick` handler and overriding the default link behavior. What you could try doing is maintaining your own history stack in app state and provide this as a target path for any link you want to use to navigate to the previous location. Note however that this still isn't a back navigation, it'll be a PUSH, not a POP. – Drew Reese Jun 21 '22 at 18:41
4

<Link to={-1}>Go Back</Link> will work.

blessedcoder
  • 160
  • 9
  • Hum, in this codesandbox, it didn't work. https://codesandbox.io/s/a11y-into-js-forked-7oq2vq?file=/src/App.js – sandrina-p Jun 19 '22 at 10:32
  • Update: I think we found a bug with React Router — it does not work on the first attempt, but it does if we go to another page first (eg /middle). However, the go back link is always "/destiny", even when it works. I will report a bug on react router repo. – sandrina-p Jun 19 '22 at 10:43
  • I just tested on that sandbox. Seems to be working. Here's my fork of it. I refactored some of the routing code to make it cleaner. https://codesandbox.io/s/a11y-into-js-forked-hx2ef2?file=/src/App.js – blessedcoder Jun 19 '22 at 10:46
  • I'm sorry but it still does not work on my side Even in another sandbox. I created an [issue on GitHub with more details](https://github.com/remix-run/react-router/issues/8994) – sandrina-p Jun 19 '22 at 11:11
  • This is flat out incorrect. The `Link` component's `to` prop takes a `Partial` object ***or*** a `string` type, not any number. https://reactrouter.com/docs/en/v6/components/link – Drew Reese Jun 20 '22 at 18:54
  • It definitely works with -1. I've been using it on multiple projects. It works as intended. Not just me, as the other person pointed, it works for them too. It does take -1 as a value no matter what their documentation says. – blessedcoder Jun 21 '22 at 12:29
  • What other person? sandrina-p said it didn't work for them. I also just retried your sandbox, and it didn't work as expected, or consistently, for me either. If it's working for you at all then I'd say that's a happy accident. – Drew Reese Jun 21 '22 at 15:24
0

As far as I know, it's impossible to have a fully accessible "Go Back" link with:

  <Link to={-1}>Go back</Link>
  // or
  <button onClick={() => navigate(-1)}>Go back</button>

It's not fully accessible for 2 reasons:

  1. Click does not work when you come directly from the first page (on mount).
  2. Right-click (open in a new tab) never works.

For 1) I found a workaround:

const router = useRouter(); // next/router

function handleGoBack() {
   //  Verify if previous page exists before using router.back
    const hasPreviousPage = window.history.length > 1;

    if (hasPreviousPage) {
      router.back();
    } else {
      // fallback to a meaningful route.
      router.push("/");
    }
}

<button onClick={handleGoBack}>
  Go back
</button>

For 2) I didn't find any solution. My recommendation would be to avoid "go back" links using -1 whenever you can, in favor of using <Link> with a real URL to the possible previous page.

sandrina-p
  • 3,794
  • 8
  • 32
  • 60
0

As described in this answer on a different question, the way you can implement the go back behaviour with accessible links in React Router 6 is to use the location state to store the path value which you want to go back to, then read it in the component which holds the link.

// COMPONENT 1
const { pathname } = useLocation();
const navigate = useNavigate();
// either of the following
<Link to="/destination" state: { previous: pathname }>
  CLICK ME
</Link>
<Button
  onClick= {() => {
    navigate(
      `/destination`,
      { state: { previous: pathname } }
    )
  }
>
  CLICK ME
</Button>

// COMPONENT 2
const { state: { previous }} = useLocation();
<Link href={ previous }>GO BACK</Link>
maja
  • 697
  • 5
  • 18
-1

This might work...

let url = '\\peviouspagename.js';
let element = <Link to={url}>Go Back</Link>

Place anywhere before your function...

In the function, you'll have something like: <div>{element}</div> where the script is normally to go. Element is the <Link> script. State the URL separate, the page name is all it needs. You can hash <div id="hash"></div> as well by adding it to to the end like so: let url = '\\peviouspagename.js#hash'; doing so should take you directly to the that <div> onClick.

HiB
  • 35
  • 6
  • I did not understand your proposal. Could you integrate it in this codesandbox? codesandbox.io/s/a11y-into-js-forked-7oq2vq?file=/src/App.js – sandrina-p Jun 19 '22 at 10:42
  • Mmm... my apologies. I am currently experiencing the same issues w/ reactjs. My current "working" solution is displayed here: https://stackoverflow.com/questions/72639765/routing-how-do-you-fail-to-find-a-page-that-is-right-in-obviously-right-in-fron It partially works for me. Instead here I'm telling it where to go explicitly rather then go back... my issue is its the same page so it takes me to the section, then tells me no page found. Ironically the same issue I found trying in your sandbox. It takes me to the page, but won't display page. – HiB Jun 20 '22 at 04:27
  • I'm going to keep an eye on this page tho. Especially on the Bug ticket. Not sure what I'm missing. Tried building a router page into my own application as well but, that didn't solve the problem either. Someone told me with the react, I have to have the page I want it to go to IN the router folder? Might recommend giving that a go. Good luck! – HiB Jun 20 '22 at 04:31