📃

React Canaryバージョンにおけるtitle要素の新たな挙動

2024/02/17に公開

Reactのカナリアバージョンではコンポーネントからtitle要素を用いることでドキュメントのタイトルを設定可能になります。

最新バージョン

まずは、現在の最新バージョンであるv18.2.0で挙動を確認します。
コンポーネント内でtitle要素を用いると、用いた場所通りにtitle要素が描画されます。

tsx
<div>
  <p>⬇︎⬇︎⬇︎タイトル⬇︎⬇︎⬇︎</p>
  <title>タイトル</title>
  <p>⬆︎⬆︎⬆︎タイトル⬆︎⬆︎⬆︎</p>
</div>

上記のようなJSXを記述した場合、描画結果も同じようになります。

html
<div>
  <p>⬇︎⬇︎⬇︎タイトル⬇︎⬇︎⬇︎</p>
  <title>タイトル</title>
  <p>⬆︎⬆︎⬆︎タイトル⬆︎⬆︎⬆︎</p>
</div>

検索エンジンやブラウザの挙動によってはbody要素内にtitleがある場合はそれがドキュメントのタイトルと読み取ってくれないことが多いのでこのような結果は避けたいです。そもそも、title要素はHTMLの標準ではhead要素内に配置するように規定されているので、body要素内に配置されるのは相応しくありません。
さらに、描画先のHTMLのhead要素にすでにtitleが設定済みな場合はドキュメントのタイトルを更新できないのでこの方法でドキュメントのタイトルを設定することは難しいです(そしてそのケースに該当することは多いと思います)。

実際にコンポーネント内でtitleを設定するデモは以下の通りです。

カナリアバージョン

次にReactのカナリアバージョンにおける挙動を確認しましょう。

tsx
<div>
  <p>⬇︎⬇︎⬇︎タイトル⬇︎⬇︎⬇︎</p>
  <title>タイトル</title>
  <p>⬆︎⬆︎⬆︎タイトル⬆︎⬆︎⬆︎</p>
</div>

先ほどと同じように上記のようなJSXを記述した場合、描画結果は以下のようになります。

html
<div>
  <p>⬇︎⬇︎⬇︎タイトル⬇︎⬇︎⬇︎</p>e>
  <p>⬆︎⬆︎⬆︎タイトル⬆︎⬆︎⬆︎</p>
</div>

該当するコンポーネントからtitle要素の部分が消えています。消えたtitle要素はhead要素内に追加されます。既にtitle要素が設定されていた場合は先頭に追加されます。
先頭に追加されるため、ドキュメントのタイトルが設定したものに変更されます。

先程と同じようなデモですが、今回はタイトルが変更されていることが確認できると思います。

注意点

複数宣言する

title要素は他の要素と同じように複数のコンポーネントで複数の箇所で宣言可能です。それらが描画されると、head要素内にtitle要素が次々追加さます。

単一のコンポーネントで複数宣言する場合のデモは以下のとおりです。

レンダリングされる順番通りにhead要素内title要素が追加されて行くように見えます。

単一のコンポーネントでしたのでどのようにtitle要素が追加されるかわかりやすかったですが、複数のコンポーネントではtitle要素がどのように追加されるか想像するのが難しくドキュメントのタイトルをうまく設定できません。
さらに今後の改修によってtitle要素がどのように追加されるかは変化する可能性もありますし、title要素が多いことで検索エンジンやブラウザ等の解釈に悪影響があるかもしれません。レンダリング毎に宣言するtitle要素はできるだけ1つとなるようにしてください。

このような背景があるので、Reactでtitle要素を設定するようにする場合は描画先のHTMLのhead要素にtitle要素を配置しない方が良いと思います。
コンポーネントで宣言したtitle要素はライフサイクルに合わせてhead要素から取り除かれます。同じライフサイクルでレンダリングされないコンポーネントからtitle要素を宣言するのは問題ありません。

children

title要素のchildrenは単一の文字列である必要があります。

下記のデモを見てください(動作を確認するためにtitle要素を複数宣言しています)。

title要素をそれぞれhello {world}{'hello ' + world}で宣言しました。head要素内を見てみると、前者は<title></title>として設定され、後者は<title>hello world</title>と設定されています。
consoleを見てください。それぞれがどのようにレンダリングされているかを確認できます。多くは変わりませんが、propschildrenが配列になっているものと文字列になっているものに分かれていると思います。

// 前者のconsole logを抜粋
{
  $$typeof: Symbol(react.element)
  key: null,
  props: {
    children: ['hello ', 'world'],
  },
  ref: null,
  type: 'title',
}

// 後者のconsole logを抜粋
{
  $$typeof: Symbol(react.element)
  key: null,
  props: {
    children: 'hello world',
  },
  ref: null,
  type: 'title',
}

title要素を利用するときはchildrenが文字列となるようにする必要があります。
自明な仕様ではありませんが、利用する際は注意してください。

例外

以下のパターンでtitleが利用された場合は挙動が最新のバージョンと同じになります。

  • svg要素内のtitle要素
  • title要素がpropsitemPropを持つ(itempropについては仕様書を確認してください)

どちらもドキュメントのタイトルに設定するとは異なる意味を持つのでこのような仕様になっています。むしろ通常の利用が例外的にhead要素内に追加されると考えた方が良いと思います。

GitHubで編集を提案

Discussion