💭

【Next.js】外部サイトのOGPを取得する方法

2024/09/22に公開

はじめに

Next.jsで開発をしていた時にOGPを取得する用途があり、調べたのでまとめました。

OGPとは

OGPとは、Open Graph Protocol の略で、ウェブサイトのコンテンツをソーシャルメディア(LINEやFacebookやXなど)で共有する際に、どのように表示されるかを指定するためのプロトコルです。
ソーシャルメディアでリンクとして共有されたときに、タイトル、画像、説明文などをカスタマイズすることで、視覚的にリッチなプレビューを提供できます。

この記事ではOGPの取得、つまりURLからその外部ウェブサイトのコンテンツ情報(タイトル、説明、画像など)を取得する方法を説明します。

OGP取得手順

ZennのURLhttps://zenn.dev/から取得できるOGPは以下です。

{
    "success": true,
    "twitterCard": "summary",
    "ogUrl": "https://zenn.dev",
    "ogTitle": "Zenn|エンジニアのための情報共有コミュニティ",
    "ogDescription": "Zennはエンジニアが技術・開発についての知見をシェアする場所です。本の販売や、読者からのバッジの受付により対価を受け取ることができます。",
    "ogType": "article",
    "ogSiteName": "Zenn",
    "ogImage": [
        {
            "url": "https://static.zenn.studio/images/logo-only-dark.png",
            "type": "png"
        }
    ],
    "ogLocale": "ja",
    "ogDate": "2024-09-19T13:41:18+00:00",
    "favicon": "https://static.zenn.studio/images/logo-transparent.png",
    "charset": "utf-8",
    "requestUrl": "https://zenn.dev/"
}

1.外部ライブラリのインストール

OGP情報の解析を簡単に行うために、open-graph-scraperライブラリを使用します。
https://www.npmjs.com/package/open-graph-scraper

npm install open-graph-scraper

2.OGP取得関数を作成

lib/getOgp.ts
import ogs from "open-graph-scraper";

export async function getOgp(url: string) {
  try {
    const { result } = await ogs({ url });
    return result;
  } catch (error) {
    console.error("Error fetching OGP:", error);
    return null;
  }
}

3.OGP取得

サーバーサイドでOGPを取得します。どちらでも可能です。

  • ①Server Components内で取得
  • ②Server Actions内で取得

①Server Components内で取得

以下はServer Components内で取得する例です。

page.tsx
import { getOgp } from "@/lib/getOgp";

export default async function Page() {
  const ogp = await getOgp("https://zenn.dev/");
  return (
    <div>
      {ogp && (
        <div className="mb-6 max-w-md rounded-lg bg-white p-4 shadow-md">
          <h2 className="mb-2 text-xl font-semibold">
            {ogp?.ogTitle ?? "No Title"}
          </h2>

          <p className="mb-4">{ogp?.ogDescription}</p>

          <div className="mb-4 flex">
            {ogp?.ogImage && ogp?.ogImage.length > 0 && (
              <img
                src={ogp?.ogImage[0]?.url}
                alt={ogp?.ogImage[0]?.alt ?? "OGP Image"}
                className="mb-2 h-auto w-48"
              />
            )}
          </div>
        </div>
      )}
    </div>
  );
}

②Server Actions内で取得

フォームにURLを追加してボタンを押した時にOGPを取得するようなユーザーのインタラクティブな操作がある場合はServer Actions内で処理を行います。
以下はServer Actionsで使用する例です。

lib/ogpActions.ts
"use server";
import { OgObject } from "open-graph-scraper/types";
import { z } from "zod";
import { getOgp } from "./getOgp";

const FormSchema = z.object({
  url: z.string(),
});

export async function ogpAction(
  prevState: OgObject | null,
  formData: FormData,
) {
  const { url } = FormSchema.parse({
    url: formData.get("url"),
  });

  try {
    const result = await getOgp(url);
    return result;
  } catch (error) {
    console.error("Error fetching OGP:", error);
    return null;
  }
}

Client ComponentsではuseFormStateを使ってServer ActionsからOGPを受け取って表示します。

page.tsx
"use client";
import { ogpAction } from "@/lib/ogpAction";
import React from "react";
import { useFormState } from "react-dom";

export const UrlBox = () => {
  const [ogp, ogpActionState] = useFormState(ogpAction, null);

  return (
    <section>
      <form
        action={ogpActionState}
        className="relative mb-6 flex w-full max-w-md items-center"
      >
        {/* 入力フィールド */}
        <input
          type="text"
          name="url"
          placeholder="URLを入力"
          className="w-full rounded-full border border-gray-300 bg-white px-6 py-3 pr-12 text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-400"
        />

        {/* ボタン */}
        <button type="submit">追加</button>
      </form>

      <div>
        {ogp && (
          <div className="mb-6 max-w-md rounded-lg bg-white p-4 shadow-md">
            <h2 className="mb-2 text-xl font-semibold">
              {ogp?.ogTitle ?? "No Title"}
            </h2>

            <p className="mb-4">{ogp?.ogDescription}</p>

            <div className="mb-4 flex">
              {ogp?.ogImage && ogp?.ogImage.length > 0 && (
                <img
                  src={ogp?.ogImage[0]?.url}
                  alt={ogp?.ogImage[0]?.alt ?? "OGP Image"}
                  className="mb-2 h-auto w-48"
                />
              )}
            </div>
          </div>
        )}
      </div>
    </section>
  );
};

まとめ

外部サイトのOGPを取得する機会があるかはわかりませんが簡単にまとめてみました。
ただいま個人開発を進めているので次回は完成次第その紹介の記事を書きたい所存!💦

Discussion