Next.js: Children in <Link> cause Invariant Violation #143

Created on 3 Apr 2017  路  14Comments  路  Source: vercel/next.js

Code

<Link prefetch route='route' params={{param: '1'}}>
  <a><span>string</span></a>
</Link>

Error

React.Children.only expected to receive a single React element child.
_Invariant Violation: Minified React error #143_

Environment

Production-only, v2.0.1

Add-on

fridays/next-routes

Workaround

Router.prefetch

please add a complete reproduction

Most helpful comment

I was able to reproduce the error

<Link prefetch href={{item.href}}><a>{item.name}</a></Link>

works, but

<Link prefetch href={{item.href}}> <a>{item.name}</a> </Link>

doesn't and gives the error you mentioned.

Notice the spaces before and after the <a> tags

@arunoda it seems like React considers the inner spaces as elements (or as null) which throws the error Invariant Violation: React.Children.only expected to receive a single React element child.

Error also occurs with only one space.

All 14 comments

Within my environment the error can be reproduced with something as simple as:

// test.js
import Link from 'next/link'

export default props => (
  <div>
    <Link href='/'>
      <a>example</a>
    </Link>
  </div>
)
import Test from './test'

// render
// ...
   <Test />
// ...

Still production-only.

Are you sure you don't have adjacent items in your link?

<Link>
   <div>...</div>
   <img />
</Link>

for instance, won't work and will give this error message

@jonaswindey Thanks for the reply Jonas.

Yes, it throws even with a single standard child. There must be something else going on.

@flagello can we have a sample repo for that.
I can't re-produce the issue.

Reopen when you add it.

@arunoda , Hi, I have the same issue, this is my component. It's very simple. Only I need repeat a list with routes. Recently, install NextJS version 2.2.0

line: node_modules/next/dist/lib/link.js:174:35

import Link from 'next/link';
import { Component } from 'react';
import PropTypes from 'prop-types';

export default class List extends Component {
  static propTypes = {
    items: PropTypes.arrayOf(PropTypes.shape({
      name: PropTypes.any.isRequired,
      href: PropTypes.any.isRequired,
    })),
  }

  static defaultProps = {
    items: [],
  }

  render() {
    const { items } = this.props;

    return (
      <ul>
        {items.map((item, key) => {
          return <li key= { key }>
                    <Link href= {item.href}> <a>{item.name}</a> </Link>
                 </li>;
        })}
      </ul>
    );
  }
}

I've seen that error before and restarting the server helped.

@wzalazar: are you also only having the error in production mode?

I was able to reproduce the error

<Link prefetch href={{item.href}}><a>{item.name}</a></Link>

works, but

<Link prefetch href={{item.href}}> <a>{item.name}</a> </Link>

doesn't and gives the error you mentioned.

Notice the spaces before and after the <a> tags

@arunoda it seems like React considers the inner spaces as elements (or as null) which throws the error Invariant Violation: React.Children.only expected to receive a single React element child.

Error also occurs with only one space.

It seems like this error is expected behaviour: https://discuss.reactjs.org/t/jsx-newline-bug/3242

Spaces are preserved in JSX so they are considered as child elements.

@jonaswindey you're right, that is the reason of bug :)

I have a scenario where this is pretty inconvenient -- in my blog I parse and render markdown with remark and remark-react, which tends to call <a> with something like ["link text"] as children. This bypasses the if (typeof children === 'string') { check in next/link and then throws at const child = Children.only(children).

I was able to work around in this case with:

function MyLink({ children, href, as }) {
  let validChildren = children;
  if (Array.isArray(children) && children.length == 1) {
    validChildren = children[0];
  }
  const props = {children: validChildren, href, as};
  return <Link {...props} />;
}

... but it would be wonderful if this just worked :)

I'm not sure exactly who's at fault in this case:

  • remark-react for passing an array? This seems okay to me.
  • react (15.5.4) for throwing on a single-element array? Possibly.
  • next for calling Children.only? It's not clear to me why next calls Children.only...

@joelburget I'm having the same issue (I think), but with js-lingui. I'm trying to create a simple example using next.js and js-lingui, but getting the Invariant Violation: React.Children.only expected to receive a single React element child. error when using a Link inside a js-lingui Trans component. All other components works fine. I guess it does something like remark-react.

So, who's to "blame"? Did you figure out anything @joelburget?

@jonespen I didn't take this any further after I figured out my workaround.

My problem got solved by putting a Text tag within the link and then adding the text inside the Text tag.

Instead of,
<Link to="/some-link">Some Link</Link>

Try,

<Link to="/some-link"><Text>Some Link</Text></Link>

Hope this helps.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lixiaoyan picture lixiaoyan  路  3Comments

jesselee34 picture jesselee34  路  3Comments

havefive picture havefive  路  3Comments

ghost picture ghost  路  3Comments

swrdfish picture swrdfish  路  3Comments