🃏

Gatsby製ブログにmdx-js/reactとIframely APIでリンクカード対応

2022/02/14に公開

長らく、個人サイトをGatsbyで作ったまま放置で、ブログに貼り付けたURLがテキストリンクのままで味気なかったので、いい加減リンクカード対応しました。

とりあえず、マークダウンファイル(md, mdx)にプロトコル付きのURLを入力すると、サムネイル、タイトル、ディスクリプションが表示されるようにしました。

完成したはこちら↓↓↓
https://mineyuji.netlify.com/blog/20220214

目指す状態

マークダウンに貼る形式を工夫すれば、リンクカード表示は大して手がかかるものではないのですが、非エンジニアでも使えるように、何も考えずに、記事の中にURLを貼り付けられた場合に動的にサムネイルとタイトルが表示されてちゃんとスタイルが当たっている状態を目指しました。

必要なプラグインをインストール

ブログはGatsby製です。
まず、GatsbyのマークダウンからHTMLに生成するフローの中で、HTMLをカスタマイズする必要があります。

公式によると、マークダウンの中でタグをカスタマイズしたい場合は、@mdx-js/reactというパッケージを使うといいよと記載してありましたので、こちらのパッケージを導入しました。

yarn add @mdx-js/react

こちらは後ほど組み込みますので、とりあえずここでは導入のみ。

iframely API key生成

リンクカードを取得するために、URLからサムネイル、タイトル情報やディスクリプション情報を取得する必要があります。

今回は、iframely APIを使って、oEmbed形式の情報を取得するようにしました。

iframely APIを利用するためには、アカウントを作成してAPI keyを生成する必要があったので、アカウント登録を行い、API keyを生成します。

API keyが生成されれば後は、URLを以下の形式のAPIのパラメータとして送れば、oEmbed形式の情報が取得できます。

APIリクエスト

iframe.ly/api/oembed?url={URL}&api_key={API_KEY}

APIレスポンス

https://iframely.com/docs/oembed-api#api-response

リンクカード開発

これで準備が整ったので、いよいよリンクカード開発です。

リンクカードコンポーネント

リンクカード生成コンポーネントを作成します。

CardLink.jsx

import React, { useState, useEffect } from 'react'

const CardLink = ({ props }) => {
    // リンクカード化するかどうかのチェックはプロトコルの有無で判断
  const checker = props.children.match(/(http)?/)[1] !== undefined
  const [embed, setEmbed] = useState({})

  const getEmbed = async () => {
    const data = await fetch(`https://iframe.ly/api/oembed?url=${props.children}&api_key=xxhogezzfugoyy...`)
      .then(res => res.json())
      .catch(error => error)
    setEmbed(data)
  }

  useEffect(() => {
    checker && getEmbed()
  }, [])


  return (
    <>
    {
      checker ? (
        <a href={embed.url} target='_blank'>
          <div>
            <div>
              <div style={{ background: `center / contain no-repeat url(${embed.thumbnail_url})` }}></div>
            </div>
            <div>
              <div>{embed.title}</div>
              <div>{embed.description}</div>
            </div>
          </div>
        </a>
      ) : (
        <a href={props.href} {...props}>{props.children}</a>
      )
    }
    </>
  )
}

export default CardLink

今回は、リンクとして判断される、<a>に対してカスタマイズを行います。
マークダウンで、テキストリンクとして作成されているものは除外したかったので、リンクカード対応させるかどうかは、httpプロトコルが含まれているかどうかというのを条件にしています。

仕様

No カードリンク → そのままテキストリンクとして返す。

[テキストリンク](https://hoge.com)

OK カードリンク → カードリンク用のDOMで返す。

https://hoge.com

iframelyには、HTMLを返するAPIも用意されていますが、今回はスタイルをオリジナルで当てたかったので、サムネイル画像、タイトル、ディスクリプション等を取得して後は自分でスタイルを当てています。(オリジナルCSS部分は省略しております)

記事ページの開発

マークダウンのカスタマイズは、@mdx-js/reactMDXProviderで行います。
対象のタグをカスタマイズ用のコンポーネントに引き渡します。

{mdx.slug}.jsx

import { MDXProvider } from '@mdx-js/react'

<MDXProvider
    components={{
      a: props => <CardLink props={props} />
    }}
  >
   <MDXRenderer>
     {data.mdx.body}
    </MDXRenderer>
</MDXProvider>

以上で、リンクカード化対応完了です!

さいごに

今回は、iframely APIを使って、oEmbed形式のデータを取得してリンクカードをオリジナルで作成しました。

大変便利なAPIで、個人ブログとして対応させるだけなれば、十分ですが、、今のブログをOSS化できるようにしたいなぁと考えているので、API keyを必要とする構成では若干難があります。

この辺りはまだまだ改善が必要そうです。

参考資料

https://iframely.com/
https://www.gatsbyjs.com/docs/how-to/routing/customizing-components/
https://oembed.com/
https://github.com/mdx-js/mdx

Discussion