How to nest links with React router?

Viewed 92

I have the following error show up in the console.

if (isReply && detailView) {
    replyUsername = (
        <Link to={`/${username}/status/${id}`}>
            <Link to={`/${parentUsername}`}><p>Replying to {parentUsername}</p></Link>
            <Link to={`/${username}`}><p>{username}</p></Link>
            <p>{date}</p>
            <p>{text}</p>
            <p>{replies.length} {likes.length}</p>
        </Link>
    )
} 

Is there a way to work around this? This is what it looks like when rendered. Clicking on the white space links to the status itself and I also want to be able to link to the usernames.

2 Answers

A link (or anchor tag) can't be a child of another anchor tag. If you just want to render 3 links then the second two need to be unnested and rendered at the same level as the first. Use a React fragment to return a single node.

if (isReply && detailView) {
  replyUsername = (
    <>
      <Link to={`/${username}/status/${id}`}>Place some link text here</Link>
      <Link to={`/${parentUsername}`}>
        <p>Replying to {parentUsername}</p>
      </Link>
      <Link to={`/${username}`}>
        <p>{username}</p>
      </Link>
      <p>{date}</p>
      <p>{text}</p>
      <p>{replies.length} {likes.length}</p>
    </>
  )
}

If you are wanting the entire outer element to be clickable then it can't be a link, but rather some other interactable element/component with an onClick handler and some imperative navigation, i.e. history.push.

Example:

if (isReply && detailView) {
  replyUsername = (
    <div onClick={() => history.push(`/${username}/status/${id}`)} tabIndex={0}>
      <Link to={`/${parentUsername}`}>
        <p>Replying to {parentUsername}</p>
      </Link>
      <Link to={`/${username}`}>
        <p>{username}</p>
      </Link>
      <p>{date}</p>
      <p>{text}</p>
      <p>{replies.length} {likes.length}</p>
    </div>
  )
}

The problem here isn't react router nesting, but just normal document structure and <a> tags.

The general answer is that it doesn't make sense to have a link containing links, and it's bad for accessibility. (Each <Link /> tag in JSX is generating an <a></a> tag in the DOM.)

Most likely, you should restructure your layout so that there is one link containing the date, text, and other information, and below that, there are the two other links you want.

To get it working like you want, I think you'll need something like this with a container element to get things positioned the way you want:

if (isReply && detailView) {
  replyUsername = (
    <div class="container">
      <Link to={`/${username}/status/${id}`}>
        <p>{date}</p>
        <p>{text}</p>
        <p>{replies.length} {likes.length}</p>
      </Link>
      <Link to={`/${parentUsername}`}>
        <p>Replying to {parentUsername}</p>
      </Link>
      <Link to={`/${username}`}>
        <p>{username}</p>
      </Link>
    </div>
  )
}

In general you want to avoid having clickable elements within other clickable elements when you can (and in your screenshot, it looks like you can) to make your tab order and accessibility make sense.

Related