💭

Next.js ではなぜ Link の中に <a>を入れる?なぜ href を Link に付ける??

2021/04/02に公開3

始め

タイトルが雑ですね笑

昨日公式サイトでこのサンプルコードを見ていたら、ふいと浮かんだ疑問をそのままタイトルにしました。

import Link from 'next/link'

function Home() {
  return (
    <ul>
      <li>
        <Link href="/">
          <a>Home</a>
        </Link>
      </li>
      <li>
        <Link href="/about">
          <a>About Us</a>
        </Link>
      </li>
      <li>
        <Link href="/blog/hello-world">
          <a>Blog Post</a>
        </Link>
      </li>
    </ul>
  )
}

export default Home

基本的な部分ですが、同じこと思った方に役に立てるかと思ってまとめます。

まずは「Linkの中に<a>がなかったらどうなる?」という純粋な疑問からといていきましょう。こういうのは直接試したほうが早いので試します。

サンプルコードは公式チュートリアルが提供してくれてるものを使いました。

<h1 className="title">
 Read{' '}
 <Link href="/posts/first-post">
  this page!
 </Link>
</h1>

もしこのようにLinkの中にタグ自体がなくて文字列だけならどうなるか試します。

なんと、自動的に文字列がaタグで囲まれましたー!Githubに公開されてるNext.jsのコードを見るとこの処理を担当してる部分が見つけられます。

// Deprecated. Warning shown by propType check. If the children provided is a string (<Link>example</Link>) we wrap it in an <a> tag
if (typeof children === 'string') {
 children = <a>{children}</a>
}

ここで注目する部分はコメントですね。

Deprecated重要度が低くて、今後廃止される可能性があるから使用しないことをオススメするという意味らしいです(初めて知った)。ですので、文字列だけ入れてもaタグで囲んでくれる機能はなくなる可能性があるため、なるべく使わないようにしましょう。

1-2. 他のタグの場合

<h1 className="title">
 Read{' '}
 <Link href="/posts/first-post">
  <span>this page!</span>
 </Link>
</h1>

次はaタグじゃなくて他のタグが入る場合です。実験ではspanタグを入れてみました。

spanタグだけなので、先みたいに自動的にaタグで囲んではくれないことがわかります。

でもクリックしたらちゃんとページ移動できたので、機能には問題ないようです。

おそらくページ移動機能がもともとLinkコンポーネントの機能なので中身のタグが変わっても影響がないのではと私は推測しています。

ここでまた「Linkの中にaタグがなくてもルーティングに問題ないのになぜaタグを入れる必要がある?」と思いました。

気になってググってみたら stackoverflow の Why using a tag in nextjs Link? という質問で答えを見つけました。

It's for semantic html and SEO
This gives your rendered html proper semantics. This will help with your sites over all SEO. You can see examples and full explanation in the docs here.

semantic html と SEO のためにaタグを入れましょう、ということですね。

Linkのお陰で<a>がなく、他のタグがあってもページ移動機能には問題ありません。が、そうするともともと<a>が持つべき機能を他のタグが持つようになるので semantic html を崩してしまいます。そして SEO にも悪影響を与えます。

semantic html を重要さをすっかり忘れていましたので、反省しました。

最後に「なぜhref<a>ではなくてLinkにつけるのか」をときたいです。

改めて公式チュートリアルに書いてるLinkの説明を見てみましょう。

<Link> allows you to do client-side navigation to a different page in the application.

太字部分が大事です。

3-2. client-side navigation

Linkを使うとクライアント側でページ遷移ができます。これがLinkaタグの一番大きな違いだと思います。

公式チュートリアルで親切に確認方法まで教えてくれてますので、見てみましょう。

  1. ブラウザのディベロッパーツールで<html>の背景を黄色に変更します。
  2. 2つのページを行き来するためリンクをクリックします。
  3. ページ遷移の間も背景が黄色のままなことが確認できます。

クライアント側でルーティングしているため、リロードすることもなく背景色もそのまま維持されています。

もしaタグでルーティングしてたらページ遷移の際にリロードが行われ、ディベロッパーツールで指定した背景色ももとに戻ります。

aタグではなくてLinkでルーティングする理由もここにあります。ページ移動の度にリロードが行われるaタグは SPA に適切ではないでしょう。

ここでまた気になったのが、「Link<a>両方hrefをつけるとどうなるのか?」でした。

<h1 className="title">
 Read{' '}
 <Link href="/posts/first-post">
  <a href="/posts/first-post">this page!</a>
 </Link>
</h1>

さっそく1で使ってたサンプルコードでをいじってやってみましょう。

結果はLinkのみhrefをつけたときと変わりませんね。<a>hrefつけてもちゃんと client-side navigation できているのかが気になったので、 3-2 で教わった通りディベロッパーツールで背景色を黄色にしたままページ移動を試しました。

リロードされてないからちゃんと client-side navigation できてますね!

Link<a>両方hrefをつけることとLinkだけhrefをつけることが同じ結果を出すので、<a>にまでhrefをつける理由がなくLinkだけつけることになったのではないかと!私は思いました!

終わり

最初にLink教わったときは単に「こういう書き方」だと言われましたけど、やはり気になった部分は検証したりググったりして解決したほうが気持ちいいですー!

GitHubで編集を提案

Discussion

defuntydefunty

ご存じかもしれませんが。。。

// Deprecated. Warning shown by propType check. If the children provided is a string (<Link>example</Link>) we wrap it in an <a> tag
if (typeof children === 'string') {
 children = <a>{children}</a>
}

ここはdeprecatedではなくなったようです。(その理由までは書いてませんが、コメントは削除されていました)
https://github.com/vercel/next.js/pull/30606

みんちゃんみんちゃん

そうなんですね!!!気づいてませんでした、、共有ありがとうございます!!d=(´▽`)=b

本田宗一郎Bot本田宗一郎Bot

とても参考になりました。ありがとうございます。
Next.js13からはLinkの子要素に<a>タグ入れると怒られるようになりましたw

Error: Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.
Learn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor