next.jsnext-link

Next.js Link component not added to DOM with child component?


There's plenty of information about how to use Next.js' Link module if the child itself has an <a> tag within it (passHref), but there seems to be no indication that this should be needed for child component that aren't creating their own <a> tag. But I have run into a situation where a "decorator" component being a child of a Link component causes the link to not get rendered at all.

import React from 'react'
import Link from 'next/link'
import type { NextPage } from 'next'

interface Props {
  children: string
}

// A simple functional component that transforms its child to more readable form
const HexString = ({ children: hex }: Props)  => {
  const shortHex = hex.substring(0, 8) + '...' + hex.substring(hex.length - 6)
  return (
    <span title={hex} className="hex-string">
      {shortHex}
    </span>
  )
}

// Page component wishing to use that decorator inside a Link
const MyPage: NextPage = () => {
  return (
    <div>
      <Link href="/index">
        <HexString>0x0102030405060708090a0b0c0d0e0f</HexString>
      </Link>
    </div>
  )
}
export default MyPage

What gets rendered to the page is only the hex-string span and its contents. The <a> tag the Link is supposed to add is not in the DOM.

How can I get the Link component to properly render an anchor tag around this styled span tag from another component?


Solution

  • This is a difference in behavior between Next.js v12 and v13. Details about this change are in https://github.com/vercel/next.js/pull/36436. This is not yet in the documentation because it's an "experimental" feature)

    In versions 12 and less, the behavior was to not include a literal anchor tag for Link:

    // Renders: <strong onClick={nextLinkClickHandler}>Hello</strong>. No `<a>` is included.
    <Link href="/about">
      <strong>Hello</strong>
    </Link>
    
    
    // Renders: <strong onClick={nextLinkClickHandler}>Hello</strong>. No `<a>` is included.
    <Link href="/about">
      <CustomComponent />
    </Link>
    

    Note this happens when the child of a Link component is either a custom component (as this question shows) or an HTML tag.

    In Next.js v13, this will be changing to always render an <a> anchor tag wrapped around those child components.

    In Next.js v12, that behavior can be opted-into by setting the experimental.newNextLinkBehavior flag.

    So, to ensure that an anchor tag is always present, either set experimental.newNextLinkBehavior to true (if using Next.js v12), or add an empty <a> tag around the child component:

    // Page component wishing to use that decorator inside a Link
    const MyPage: NextPage = () => {
      return (
        <div>
          <Link href="/index">
            <a><HexString>0x0102030405060708090a0b0c0d0e0f</HexString></a>
          </Link>
        </div>
      )
    }