15

I have a React component that is clickable as a whole, but also contains buttons inside.

So something like

<Link to={'/path1'}>
  ...
  <Link to={'path2'} />
  ...
</Link> 

This is the behaviour I want, but I get this warning:

Warning: validateDOMNesting(...): <a> cannot appear as a descendant of <a>. See SearchResult > Link > a > ... > Link > a.

How seriously should I take this and what would be the workaround?

foobar
  • 3,849
  • 8
  • 22
  • 32
  • 2
    Why do you need to nest Link tags – Shubham Khatri Nov 14 '17 at 10:14
  • 1
    I have an item/component with a background image, a short description and a button. If you click anywhere on the background image you are taken to a detailed page, if you click on a button you are taken to a different page. That's my use case. – foobar Nov 14 '17 at 10:21
  • As the warning says you shouldn't nest anchor tags, What you could do instead is do dynamic routing through button. Attach an onClick event on the button and dynamically change the route to the inteded one, also don't forget to write `e.preventDefault()` on the button's onClick. Check this answer on how to programatically route to another url https://stackoverflow.com/questions/44127739/programatically-navigate-using-react-router/44128108#44128108 – Shubham Khatri Nov 14 '17 at 10:24
  • I'd love to find a solution to this problem as well. In my case, we have many components rendering divs inside of our table. Some are avoidable (MUI v4 Boxes, MUI v4 Grids), and some are not avoidable (MUI v4 Circular Progress). I would love to be able to suppress these errors. – Liran H Dec 09 '21 at 09:41

2 Answers2

15

Nesting anchor tags (which is what <Link /> transpiles to) is forbidden in HTML. Even if you get the desired result, it is only because your browser is clever and has its own work-arounds. However, that behavior cannot be guaranteed across all browsers and platforms.

How seriously should I take this?

As per the above, I'd say quite seriously.

What would be the workaround?

The following isn't a workaround, but what I consider to be the "proper" implementation.

I'd programatically implement the navigation for the wrapping element and use the <Link /> for the buttons. So something like:

navigate = () => {
  //push "path1" to history
}

render() {
  return(
    <div onClick={this.navigate}>
      <Link to="path2">Some button</Link>
      <Link to="path3">Some button</Link>
    </div>
  );
}

For more info about programatically navigating in React Router see one of the links below:

  • For React Router v1-v3: Link
  • For React Router v4: Link
foobar
  • 3,849
  • 8
  • 22
  • 32
Chris
  • 57,622
  • 19
  • 111
  • 137
  • 1
    Yes, that makes sense. Got it to work with react-router's browserHistory. Thank you :) – foobar Nov 14 '17 at 10:48
  • Actually I spoke to soon, it turns out with this implementation, clicking the internal button takes you to the same page as if you were to click the whole item. I solved it by adding e.stopPropagation(); to the internal button:
    { e.stopPropagation(); browserHistory.push('/path') }}>
    – foobar Nov 14 '17 at 11:43
  • This solution may help in silencing the React warning but it still has similar issues as reported by React; interactive elements should not be nested because they create accessibility issues, *that is the main reason* why HTML forbids nested anchor tags and React is just trying to be ahead of you to save some time warning about the issue. A clickable
    in this case has its own set of problems, especially for users of assistive technologies - these users will _never_ have this div announced to them, in a screen reader, for example, because a
    has no semantic meaning
    – Erick Wilder Jul 01 '22 at 15:40
3

I think the best approach is something similar to what Chris said but without the need to make the navigation programmatically. Keep the default behaviour of the links and use CSS to make them appear nested. As far as the browser is concerned, the links will be siblings and not parent/child.

So basically:

  • set the surrounding div's position to relative.
  • no explicit position to the 'parent' link, and let it use the whole width/height
  • set the 'child' link's position to absolute, which will remove it from the normal flow and will allow you to put it anywhere inside the div. Events shouldn't conflict because the links are not nested.

EDIT: I know this answer comes years later, but I expect many people to come across this problem since it is a common UI pattern.

Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
ElDuderino
  • 91
  • 1
  • 2
  • 11