• 最終更新日:

【React】react-helmet-asyncを使ったheadの設定方法 - URLごとに切り替える

【React】react-helmet-asyncを使ったheadの設定 - URLごとに切り替える

今回はreact-helmet-asyncを使ってheadの設定方法について説明します。

前提条件は以下

  • VSCode上でcreat react appを使ってReact、TypeScriptの環境で行う
  • ESLint(Airbnb)とPrettierを追加して、VSCode上の拡張機能と連動させている
  • URLごとにheadの中を変更させる(titleやdescription、canonicalなど)
  • OGPの設定はしない

環境は以下です。

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
  • React v17.0.2
  • TypeScript v4.5.5
  • react-router-dom v6.2.2
  • react-helmet-async v1.2.3
  • node.js v16.13.1
この記事の目次

react-helmet-asyncを使ってheadの設定する

前提として開発環境は以下の記事に沿って構築しています。この環境に追加してreact-helmet-asyncをインストールする流れになります。

react-helmet-asyncをインストール

react-helmet-asyncと型をインストールします。

npm i --save react-helmet-async @types/react-helmet-async

react-helmet-asyncの詳細については以下をどうぞ。

ディレクトリの構成について

トップページとpage1の2ページがあるイメージです。
この2つの表示を切り替えたときに、headの中を書き換えます。

表示の切り替えはApp.tsx内でreact routerを使って行います。

|_public/
|  |- index.html
|
|_src/
   |- assets/ (共通で使うCSSや画像をまとめている)
   |   |- css/
   |   |   |- _base.scss
   |   |   |- _custom-property.scss
   |   |   |- styles.scss
   |   |
   |   |- img/
   |
   |- components/(コンポーネントをまとめている)
   |   |- blocks/(ページを構成するコンポーネント)
   |   |   |- HeadBlock.tsx
   |   |
   |   |- pages/(全ページを管理する)
   |   |   |- home/(トップページ)
   |   |   |   |- Home.module.scss
   |   |   |   |- Home.tsx
   |   |   |   
   |   |   |- page1/(page1ページ)
   |   |   |   |- Page1.module.scss
   |   |   |   |- Page1.tsx
   |
   |- App.tsx
   |- index.tsx

基本的な書き方

まずはreact-helmet-asyncの基本的な書き方について説明します。

import React from 'react';
import ReactDOM from 'react-dom';
import { Helmet, HelmetProvider } from 'react-helmet-async';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <HelmetProvider>
      <Helmet>
        <title>サイトのタイトルです</title>
        <meta name="description" content="サイトの説明文です" />
      </Helmet>
      <App />
    </HelmetProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

メインコンポーネント(<App/>の部分)をHelmetProviderで囲み、メインコンポーネントの上にHelmetを配置します。あとはHelmetの中にheadの中に書きたいtileやdescriptionなどの指定します。

react-helmet-asyncによってheadの指定するので、index.html側のheadにはtitleの指定だけにしておきます。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <title>サイトのタイトルです</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

基本的な使い方はこれだけです。
それではURLごとにheadの表示を切り替える方法について説明します。

URLごとにheadの中を切り替える

App.tsxでreact routerを使ってトップページとpage1ページの表示を切り替えるとします。

import { BrowserRouter, Route, Routes } from 'react-router-dom';
import { Home } from './components/pages/home/Home';
import { Page1 } from './components/pages/page1/Page1';

const App = () => (
  <BrowserRouter>
    <Routes>
        <Route path="/" element={<Home />} />
        <Route path="page1" element={<Page1 />} />
    </Routes>
  </BrowserRouter>
);

export default App;

App.tsxを読み込むindex.tsxではHelmetProviderを使ってAppコンポーネントを囲っておきます。

import React from 'react';
import ReactDOM from 'react-dom';
import './assets/css/styles.scss';
import { HelmetProvider } from 'react-helmet-async';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <HelmetProvider>
      <App />
    </HelmetProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

head用のコンポーネントを作る

blocksディレクトリにHeadBlock.tsxの名前でhead用のコンポーネントを作成します。例として以下の3つをURLごとに切り替えます。

  • title
  • description
  • canonical
import { VFC } from 'react';
import { Helmet } from 'react-helmet-async';

type Props = {
  title?: string;
  description?: string;
  path?: string;
};

export const HeadBlock: VFC<Props> = (props) => {
  const { title, description, path } = props;
  return (
    <Helmet>
      <title>{title ?? 'デフォルトのタイトルです'}</title>
      <meta name="description" content={description ?? 'デフォルトの説明文です'} />
      <link rel="canonical" href={`https:hoge.com/${path ?? ''}`} />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </Helmet>
  );
};

ページごとにheadコンポーネントを読み込む

たとえばトップページでheadコンポーネントを読み込む場合では、Home.tsxに以下のように書きます。

import { HeadBlock } from '@/components/blocks/HeadBlock';
import { Link } from 'react-router-dom';

export const Home = () => (
  <>
    <HeadBlock />
    <div>
      <div>トップページです</div>
    </div>
  </>
);

トップページなのでheadコンポーネントに書いているデフォルト値を適用させるため、titleなどの指定はしていません。

トップページをディベロッパーツールで見てみると、titleなどにheadコンポーネントに指定したものが設定できているのが分かります。

ではpage1ページでheadコンポーネントを設定してみます。

import { HeadBlock } from '@/components/blocks/HeadBlock';

export const Page1 = () => (
  <>
    <HeadBlock title="page1のタイトルです" description="page1の説明文です" path="page1" />
    <div>
      <div>page1のページです</div>
    </div>
  </>
);

HeadBlockコンポーネントにtitleとdescription、pathをpropsとして渡しています。


page1ページをディベロッパーツールで確認すると以下のように反映されているのが分かります。

OGPの設定はできるのか?

ReactではJavaScriptによって動的にHTMLファイルの書き換えが行われます。titleやdescriptionなどはGoogleクロームがJavaScriptも含めて読み込んでくれるので正しく反映してくれますが、FacebookやTwitterで使用するOGP設定についてはうまく機能しません。

OGPの設定をしたい場合はサーバー側と連携するか、ReactのフレームワークであるNext.jsやGatsby.jsを使うことになります。

トップページのみOGPを適用させたいのであればreact-helmet-asyncは使わず、index.htmlのheadに直接設定しましょう。

おわりに

今回はreact-helmet-asyncを使ってheadの設定方法について説明しました。URLごとにheadの中を書き換えることはできましたが、OGPについては機能しないことを覚えておきましょう。