🏞️

動的にメタ情報を設定し、SNS に表示するタイトルやサムネも動的に設定する方法

に公開

📎 はじめに

本記事では、Next.js の App Router で動的にメタ情報を設定し、SNS にリンクを貼った時にタイトルやサムネをいい感じに表示する方法をまとめます。

📎 やりたいこと

たとえば、X に YouTube のリンクを貼ると、以下のようにタイトルとサムネがいい感じに表示されます。これを自身のサービスでもやりたいと思います。

📎 対象サービス

https://clipsearch.jp/

自身が運営するサービス、「ClipSearch」を対象に今回実践してみます。ClipSearch は、「YouTube の動画 URL から切り抜きやショート動画など関連動画を簡単に見つけるサービス」です。

詳細は以下の記事にまとめているので、ぜひご覧ください。

https://zenn.dev/yossyxp/articles/16e1a70c10ad84

📎 ディレクトリ構成

ClipSearch は Next.js の AppRouter で作っており、ディレクトリ構成は以下のようになっています。

/
└── src/
    ├── app/
    │   └── search/
    │       └── [videoId]/
    │           └── page.tsx
    ├── layout.tsx
    └── page.tsx

YouTube の URL 入力してその関連動画を表示するサービスなのでパスとして、/search/${videoId} としており、この videoId ごとにタイトルやサムネイルを変えることをゴールとします。

📎 実践

公式ドキュメント

メタ情報の設定について、公式ドキュメントを確認していきます。

https://nextjs.org/docs/app/api-reference/functions/generate-metadata

import type { Metadata } from 'next'
 
// either Static metadata
export const metadata: Metadata = {
  title: '...',
}
 
// or Dynamic metadata
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}

layout.tsxpage.tsx に対して、静的なメタ情報は metadata を動的なものは generateMetadata を設定するようです。

今回は動的にしたいので、generateMetadata を使用するとよさそうですね。

実際のコード

以下を [videoId] 配下の page.tsx に記載することで title と description が個別に指定できました。

export async function generateMetadata({
  params,
}: {
  params: Promise<{ videoId: string }>;
}): Promise<Metadata> {
  const resolvedParams = await params;
  const { videoId } = resolvedParams;
  const data = await fetchVideoData(videoId);
  const videoData = data.items?.[0];

  if (!videoData) {
    return metadata;
  }

  const videoMetaData = {
    title: videoData.snippet.title,
    description: videoData.snippet.description
  };

  return videoMetaData;
}

fetchVideoData は videoId を元に YouTube の動画情報を取得するメソッドになっています。

SNS にも表示したい

今回、SNS にもタイトルやサムネイルを表示したいので、 videoMetaDataopenGraphtwitter を追加します。

const videoMetaData = {
  title: videoData.snippet.title,
  description: videoData.snippet.description,
+ openGraph: {
+   title: videoData.snippet.title,
+   description: videoData.snippet.description,
+   url: `https://clipsearch.jp/search/${videoId}`,
+   siteName: 'ClipSearch',
+   images: [
+     {
+       url: videoData.snippet.thumbnails.high.url,
+     },
+   ],
+ },
+ twitter: {
+   card: 'summary_large_image',
+   title: videoData.snippet.title,
+   description: videoData.snippet.description,
+   images: [
+     {
+       url: videoData.snippet.thumbnails.high.url,
+     },
+   ],
+ },
};

📎 全体のコード

最終的なコードは以下のようになりました。

export async function generateMetadata({
  params,
}: {
  params: Promise<{ videoId: string }>;
}): Promise<Metadata> {
  const resolvedParams = await params;
  const { videoId } = resolvedParams;
  const data = await fetchVideoData(videoId);
  const videoData = data.items?.[0];

  if (!videoData) {
    return metadata;
  }

  const videoMetaData = {
    title: videoData.snippet.title,
    description: videoData.snippet.description,
    openGraph: {
      title: videoData.snippet.title,
      description: videoData.snippet.description,
      url: `https://clipsearch.jp/search/${videoId}`,
      siteName: 'ClipSearch',
      images: [
        {
          url: videoData.snippet.thumbnails.high.url,
        },
      ],
    },
    twitter: {
      card: 'summary_large_image',
      title: videoData.snippet.title,
      description: videoData.snippet.description,
      images: [
        {
          url: videoData.snippet.thumbnails.high.url,
        },
      ],
    },
  };

  return videoMetaData;
}

そして、結果はこちらです!

サムネが目立つ表示となりました。

Zenn の記事でもリンクを貼るだけで以下のようになります。

https://clipsearch.jp/search/X1kkjAtW7gc

📎 さいごに

ここまで読んでいただき、ありがとうございました。
X でサービスの宣伝をする際に、リンクだけだと味気ないかつ目立たないなと思い、今回実践しました。
皆様の問題解決につながれば幸いです。

Discussion