ui icon indicating copy to clipboard operation
ui copied to clipboard

Error when wrapping TableRow over link in NextJS with Shadcn

Open suenalaba opened this issue 1 year ago • 5 comments

On the latest version of NextJS 13 and ShadCN, the following code snippet will cause an error:

<Table>
  <TableCaption>A list of instruments.</TableCaption>
  <TableHeader>
    <TableRow>
      {headers.map((header: HeaderType) => (
        <TableHead key={header.name} className={header.width}>
          {header.name}
        </TableHead>
      ))}
    </TableRow>
  </TableHeader>
  <TableBody>
    {instruments.map((instrument: InstrumentType) => (
      <Link
        key={instrument.instrumentId}
        href={`/blog/${instrument.instrumentId}`}
      >
        <TableRow>
          <TableCell>{instrument.instrumentId}</TableCell>
          <TableCell>{instrument.instrumentName}</TableCell>
        </TableRow>
      </Link>
    ))}
  </TableBody>
</Table>

Is there a workaround this? I want to make it such that when a table row is clicked, it navigates to a specific instrument based on ID.

5 errors are being thrown:

Expected server HTML to contain a matching <table> in <div>.
Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
An error occurred during hydration. The server HTML was replaced with client content in <#document>.
validateDOMNesting(...): <tr> cannot appear as a child of <a>.
Warning: validateDOMNesting(...): <a> cannot appear as a child of <tbody>.

suenalaba avatar Sep 19 '23 13:09 suenalaba

Able to reproduce on this and also looking for a solution! For now, the easiest way to solve this is by using legacyBehavior on the Link component, which is not pretty but gets the job done.

itsmingjie avatar Sep 29 '23 18:09 itsmingjie

@itsmingjie hey, yeah using legacyBehavior=true solves the issue, however, when you hover over the Link component, the URL preview doesnt appear. Is this the same for you?

suenalaba avatar Oct 07 '23 14:10 suenalaba

@itsmingjie hey, yeah using legacyBehavior=true solves the issue, however, when you hover over the Link component, the URL preview doesnt appear. Is this the same for you?

@suenalaba I believe that's because the legacy Link component isn't actually a real DOM Element, but only a routing layer that adds an onClick handler to its child element. See https://github.com/vercel/next.js/pull/36436 for when they discussed how they changed it from the legacy behavior to the new one.

You'll need to nest an anchor element to have the link preview display, I believe.

itsmingjie avatar Oct 07 '23 15:10 itsmingjie

were you able to solve this?

bemartin94 avatar Feb 06 '24 00:02 bemartin94

were you able to solve this?

Only able to resolve it via using legacyBehavior=True

suenalaba avatar Feb 09 '24 14:02 suenalaba

If using a client component, you can also use the router via useRouter() to achieve this directly on the TableRow:

<TableRow
    onClick={() => {
         router.push(`/your-url`)
    }}
    className="cursor-pointer"
>

This doesn't provide the browser URL preview but it avoids using legacyBehavior. I know this is basically the same thing, but at least you will have control over it, and this protects against if the legacyBehavior attribute is removed in the future.

shanehoban avatar Mar 12 '24 22:03 shanehoban

@shanehoban I guess if you wanted to do this without converting into a client component you could make the whole row a form with a redirect action and the whole row being a submit button, haven't tested tho, I'm ok with the legacyBehavior for now

adriangalilea avatar Apr 03 '24 09:04 adriangalilea

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.

shadcn avatar Jun 28 '24 23:06 shadcn