Next.jsでmeta情報をSSRする

Next.jsを使った際にmeta情報をSSRする手順を備忘録として残しておきます。

Next.jsでＳＳＲする際は、`getInitialProps`を使うと思います。
[こちらのコードを参考](https://github.com/zeit/next.js/blob/canary/examples/data-fetch/pages/index.js)にして、`getInitialProps`で取得したデータをもとにmeta情報を格納していけば、SSR可能です。
(非常に単純なことしか書いていない気が今更ながらしてきました。)

ここでは例として`クエリパラメータ`として受け取った値をもとにmeta情報を作成するサンプルを下に書き残していきます。

ちなみに`TypeScript`で書いていますが、書き慣れていない感がかなり出ていると思います。ご了承ください。


## Next.js 9 (TypeScript) セットアップ

```bash
yarn add next@latest react@latest react-dom@latest
```


```bash
mkdir pages
vim pages/index.tsx
```

`tsx`ファイルを設置します。

```tsx
export default () => <p>Hello World</p>;
```

nextサーバを立ち上げます。

```bash
yarn next dev
# 多分下記のコマンドでも変わらない気がしています
# yarn next
```

と、このコマンドを実行すると、下記のコマンドを実行しろとエラーが出てくるので、言われたとおりに下記のコマンドを実行します。

```bash
yarn add --dev typescript @types/react @types/node
```

ここまで実行すると、カレントディレクトリに下記ファイルが生成されていることが確認できます。

 * tsconfig.json
 * next-env.d.ts


Typescriptで書き始めるまでの準備はここまでとなります。
なお、`localhost:3000`にアクセスすると、`Hello World`が表示されているのが確認できます。

## TypeScriptサンプル(型チェック)

実際に型のチェックをしてくれるかをチェックしてみます。

```tsx
const greeter = (person: string) => {
  return 'Hello ' + person
}

export default () => (
   <p>{greeter('World')}</p>
)
```

`Hello World`が表示されます。
今度は下記のように書き換えてみます。

```tsx
const greeter = (person: string) => {
  return 'Hello ' + person
}

export default () => (
   <p>{greeter(123)}</p>
)
```

すると、Next.js側でConsoleに下記のようなエラーを吐き出してくれます。

```bash
6:16 Argument of type '123' is not assignable to parameter of type 'string'.
    4 |
    5 | export default () => (
  > 6 |    <p>{greeter(123)}</p>
      |                ^
    7 | )
```

## Next.jsで動的にメタ情報を設定＆SSRする

`components`ディレクトリに`head.tsx`を作成し、こちらにメタ情報表示のための処理を書いていきます。
(ファイル名やディレクトリ名は任意なので、`components`じゃなくてもOKです)


```bash
mkdir components
vim components/head.tsx
```

なおここの`head.tsx`については、[こちらの記事(Next.jsでタイトルやメタタグやOGPなどのHeadタグを動的に設置するサンプル)](https://t-cr.jp/memo/2201639257480a359)の記述を、ほぼほぼそのまま引用させていただいています。


```tsx
/*
 * 下記ページの内容をほぼほぼそのまま引用させていただいています。
 * https://t-cr.jp/memo/2201639257480a359
 */
import Head from 'next/head';

interface Props {
  title: string;
  description: string;
  keyword: string;
  image: string;
  url: string;
}

export default ({
  title,
  description,
  keyword,
  image,
  url
}: Props): JSX.Element => {
  return (
    <Head>
      <title>{title}</title>
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta name="keywords" content={keyword} />
      <meta property="og:type" content="blog" />
      <meta property="og:url" content={url} />
      <meta property="og:image" content={image} />
      <meta property="og:site_name" content={title} />
      <meta name="twitter:card" content="summary" />
      <meta name="twitter:site" content="@tcr_jp" />
      <meta name="twitter:url" content={image} />
      <meta name="twitter:title" content={title} />
      <meta name="twitter:description" content={description} />
      <meta name="twitter:image" content={image} />
      <link rel="canonical" href={url} />
      <link
        rel="shortcut icon"
        href={
          'https://cdn.qiita.com/assets/favicons/public/production-4ff10c1e1e2b5fcb353ff9cafdd56c70.ico'
        }
      />
      <link
        rel="apple-touch-icon"
        href={
          'https://cdn.qiita.com/assets/favicons/public/apple-touch-icon-f9a6afad761ec2306e10db2736187c8b.png'
        }
      />
    </Head>
  );
};
```

上記の`head.tsx`を`pages/index.tsx`で読み込みます。

```tsx
import Head from '../components/head';

const greeter = (person: string) => {
  return 'Hello ' + person;
};

export default () => (
  <div>
    <Head
      title={'タイトルです'}
      description={'ディスクリプションです'}
      keyword={'キーワードです！'}
      image={
        'https://qiita-user-profile-images.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2F-BUmWankl_aQ%2FAAAAAAAAAAI%2FAAAAAAAADys%2F8oi87glPMLA%2Fphoto.jpg%3Fsz%3D50?ixlib=rb-1.2.2&auto=compress%2Cformat&lossless=0&w=300&s=649d309c71e365e6fc6d6b6d205c3710'
      }
      url={'https://qiita.com/shinshin86'}
    />
    <p>{greeter('World')}</p>
  </div>
);
```

これで`yarn next dev`をして`localhost:3000`にアクセスし、右クリックからページのソースコードを表示するとmeta情報がSSRされているのが確認できます。


## 動的な値を元にしてメタ情報をSSRする

受け取ったクエリパラメータを元にメタ情報をＳＳＲするため、`pages/index.tsx`を下記のように書き換えます。

```tsx
import Head from '../components/head';

const greeter = (person: string) => {
  return 'Hello ' + person;
};

interface Props {
  title: string;
  description: string;
  keyword: string;
  image: string;
  url: string;
  text: string;
}

const Index = ({ title, description, keyword, image, url, text }: Props) => (
  <div>
    <Head
      title={title}
      description={description}
      keyword={keyword}
      image={image}
      url={url}
    />
    <p>{greeter(text)}</p>
  </div>
);

Index.getInitialProps = ({ query }) => {
  const { title, description, keyword, image, url, text } = query;
  return { title, description, keyword, image, url, text };
};

export default Index;
```

これで`getInitialProps`内で受け取ったクエリパラメータを元に描画を行うようになります。
`yarn next dev`でサーバを立ち上げて、下記のURLでアクセスすると、クエリパラメータとして渡した値を元にＳＳＲされているのが確認できます。
(それにしてもとても長いURLになってしまった...)

http://localhost:3000/?title=%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%81%A7%E3%81%99&description=%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E3%81%99&keyword=%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89%E3%81%A7%E3%81%99%EF%BC%81&image=https://qiita-user-profile-images.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2F-BUmWankl_aQ%2FAAAAAAAAAAI%2FAAAAAAAADys%2F8oi87glPMLA%2Fphoto.jpg%3Fsz%3D50?ixlib=rb-1.2.2&auto=compress%2Cformat&lossless=0&w=300&s=649d309c71e365e6fc6d6b6d205c3710&https://qiita.com/shinshin86&text=world!!!!!!!!!!!!


## 参照

[Blog - Next.js 9](https://nextjs.org/blog/next-9)

[⚡︎ Next.js 9が出た：ご機嫌な話 ⚡︎](https://qiita.com/NanimonoDemonai/items/766ccc8602ea3fe90b48)

[Next.jsでタイトルやメタタグやOGPなどのHeadタグを動的に設置するサンプル](https://t-cr.jp/memo/2201639257480a359)