React + Next.js: Linkコンポーネントで<a>要素を包んだらサーバーエラーになった

`Link`コンポーネントで`<a>`要素を包むというのは、Next.jsのサンプルでよく見かけるコードです。ところが、Next.jsの最新バージョンでは、サーバーエラーが起こるようになりました。本稿は、その理由と解決方法についてご説明します。

## エラーの内容
エラーで告げられるメッセージはつぎのとおりです。

> Server Error
Error: Invalid `<Link>` with `<a>` child. Please remove `<a>` or use `<Link legacyBehavior>`.

解決方法も示されています。けれど、なぜエラーが生じるようになったのか、わからないことには納得いきません。


## なぜエラーが起こったのか
これまで(Next.js 12.2より前)は、`Link`コンポーネントは子に`<a>`要素を含むことが求められてきました。それが、もはや要らなくなったのです。


> **Note**: Before Next.js 12.2, it was required that the Link component wrapped an `<a>` tag, but [this is not required in versions 12.2 and above](https://nextjs.org/blog/next-12-2#:~:text=next/link%20no%20longer%20requires%20manually%20adding%20%3Ca%3E%20as%20a%20child.%20You%20can%20now%20opt%20into%20this%20behavior%20in%20a%20backward%2Dcompatible%20way.)(「[Navigate Between Pages](https://nextjs.org/learn/basics/navigate-between-pages/link-component)」).

むしろ、冒頭で見たとおり、`<a>`タグを子要素に含むとエラーになってしまいます。`Link`コンポーネントが`<a>`要素に置き替えられるため、入れ子になるからです。


# どう対応すればよいか
エラーの解消には、3つのやり方があります。

## `<a>`要素を除く
第1は、エラーメッセージのとおり、`<a>`タグを外すことです。要らないものは除くのが、もっとも端的でしょう。「[next/link](https://nextjs.org/docs/api-reference/next/link)」のサンプルコードも、あっさりそれを基本としています。

## `legacyBehavior`プロパティを加える
第2に、すでにアプリケーションがつくられている場合、`<a>`タグをすべて消そうとすると問題が起きるかもしれません。そのために用意されたのが`legacyBehavior`プロパティです。`Link`コンポーネントに含めた`<a>`要素の扱いをこれまでと同じにしてくれます。

> `legacyBehavior` - Changes behavior so that child must be `<a>`. Defaults to false(「[If the child is `<a>` tag](https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-tag)」).

```react
import Link from 'next/link'

function Legacy() {
	return (
		<Link href="/about" legacyBehavior>
			<a>About Us</a>
		</Link>
	)
}

export default Legacy
```

> The next/link child can no longer be `<a>`. Add the `legacyBehavior` prop to use the legacy behavior or remove the `<a>` to upgrade. A [codemod is available](https://nextjs.org/docs/advanced-features/codemods#new-link) to automatically upgrade your code(「[Breaking Changes](https://nextjs.org/blog/next-13#breaking-changes)」).

`legacyBehavior`を使えば、以前のバージョンとの互換性も保てるのです。

> next/link no longer requires manually adding `<a>` as a child. You can now [opt into this behavior](https://github.com/vercel/next.js/pull/36436) in a backward-compatible way(「[Other Improvements](https://nextjs.org/blog/next-12-2#other-improvements)」).


## codemodを使う
第3の解決方法は、公式サイトにはごくあっさりと説明されています。codemodを用いることです(「[Invalid `<Link>` with `<a>` child](https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor)」参照)。これまでの`Link`コンポーネントの子の`<a>`タグも、新しいバージョンに対応させてくれます。

プロジェクトのルートで、codemodをつぎのように実行してください。

> To run the codemod, run `npx @next/codemod new-link` in the root of your project(「[Rework `<Link>` behavior (backwards compatible)](https://github.com/vercel/next.js/pull/36436)」).

```terminal
npx @next/codemod new-link
```


# まとめ
「Upgrade Guide」の「[v13 Summary](https://nextjs.org/docs/upgrading#v13-summary)」に対応がまとまっています。

1. `Link`コンポーネントの子の`<a>`タグを外す。
2. `Link`コンポーネントに`legacyBehavior`を加える。
3. アプリケーションのルートでcodemodを実行する。

> The next/link child can no longer be `<a>`. Add the legacyBehavior prop to use the legacy behavior or remove the `<a>` to upgrade. A [codemod is available](https://nextjs.org/docs/advanced-features/codemods#new-link) to automatically upgrade your code.