next/linkのlegacyBehaviorを使ってaタグの中にbuttonタグが入らないようにする

next/linkが提供しているLinkコンポーネントを使用していた際、挙動としては問題なく動くもののHTMLの仕様的によろしくない実装をしてしまっていました。

具体的にはaタグの中にbuttonタグを入れ子にしてしまっていました。
MDNのaタグのページを見るとPermitted contentの欄にinteractive contentsをいれてはいけないと書いてあります。
buttonはinteractive contentsに相当するのでまずそうです。
入れても特にconsole上でエラーとかは見られなかったんですが、おそらく役割が不明瞭になるという点でアクセシビリティ上よくないってことなのではと推測しています。

というわけで、これを解消するというのが本記事の主題です。
ただ内容はNext.jsの公式ドキュメントに書いてあるlegacyBehaviorを使う方法で、特に目新しいものはありません。
HTMLの仕様を意識してコードを書こうという自戒を込めて記事にしておきます。

動作確認バージョン
Next 13.1.4

目次

問題になっていたコード

Next.jsのv13ではnext/linkで提供されているLinkコンポーネントがaタグを内包するようになりました。

あわせて読みたい
Building Your Application: Upgrading | Next.js Learn how to upgrade to the latest versions of Next.js.
import Link from 'next/link'

// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  <a>About</a>
</Link>

// Next.js 13: `<Link>` always renders `<a>` under the hood
<Link href="/about">
  About
</Link>

画面遷移するUIを表現する際、見た目がボタンであることはわりと一般的なんじゃないかなと思います。
今回もそのようなUIだったので、当初以下のように画面遷移するボタンを表現していました。
(ちなみにButtonコンポーネントはMantineというUIライブラリのものを使っています)

<Link href="/foo">
  <Button>fooへ</Button>
</Link>

しかし、これだとLinkタグがaタグを内包しているのでレンダリング結果は以下のようになってしまい、aタグの中にbuttonタグが含まれてします。

<a href="/foo">
  <button>fooへ</button>
</a>

これはHTMLの仕様的によろしくないので対策しないといけません。

legacyBehaviorを使う

対策の仕方としてはbuttonタグを使わずにボタンの見た目になるような実装が必要です。

頑張ってCSSでゴリ押ししてもいいんですが、あまりやりたくないので今回はUIコンポーネントの仕様を活かす方向で考えます。
MantineではButtonコンポーネントにcomponentのpropsがあり、component="a"とすると見た目そのままにaタグとしてレンダリングされます。

Next.jsのv13から導入されたlegacyBehabiorはv12までの仕様に戻すものであり、つまりLinkコンポーネント自体がaタグを内包しなくなります。

これらを組み合わせると無事、見た目はこれまで通りボタンでaタグとして画面遷移するものが出来上がります。
(passHrefは小要素のhrefpropsにLinkのhrefを渡すためのもの)

<Link href="/foo" legacyBehavior passHref>
  <Button component="a">fooへ</Button>
</Link>
// → <a href="/foo">fooへ</a>

ちなみにコンポーネントをaタグにしなくても挙動としては変わらないが、その場合はbuttonタグで画面遷移していることになってしまうので避けています。

buttonタグどころかコードをパッと見た感じだとおそらくonClickが生えているものならなんでも良さそうなので、内部でonClick で画面遷移させていそう?(ちゃんと読み解けておらず、自信はないです…)

まとめ

aタグの中にbuttonタグが入らないように回避する方法として、今回はbuttonコンポーネントを見た目そのままにaタグにしてlegacyBehaviorを使いました。

MantineのButtonコンポーネントにaタグにするためのpropsがあってよかった…

あとこれまでHTMLの仕様とかそんなに意識せずに書いてしまってたなーという反省。

できるところからちゃんと意識して書いていこうと思いました。

この記事が参考になったからコーヒーくらいおごってもいいぜという方は、以下からサポートいただけると次の記事書くモチベになりますのでよろしくお願いします

Buy Me A Coffee

参考

あわせて読みたい
Components: | Next.js API reference for the component.
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次