Gatsbyブログにブログカード・ブログタイトルを挿入する(2022/9/3追記あり)

2022/4/03

(最終更新: 2022/9/03

はじめに

Gatsbyブログに、はてなブログのようなブログカードを挿入します。

このブログは元々はてなブログで書いていたので、同じようなことがしたかったです。

はてなブログのブログカード はてなブログのブログカード

はてなブログのブログタイトル はてなブログのブログタイトル

2022/9/3追記

完成したもの

MDXファイル内にURLを記載すると、指定したURLの情報が、Gatsbyのコンポーネント内で取得できるようになりました。 URLの情報が取得できる

これらの情報を利用して、自分の好きなデザインのブログカードや、タイトルのみの表示を可能としました。

使うもの

IframelyのAPIを使います。

参考資料

Iframely - Embeds codes for today's Internet

IframelyのAPIについて

まずはIframelyに無料アカウント登録します。無料で月に1,000クエリまで叩けるようです。

するとIframly登録後ページに、API keyが払いだされます。

Iframly登録後ページ

右側にあるのが、Iframlyの提供するAPIのエンドポイントです。

2つありますが、今回は

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

こちらを使おうと思います。

Iframely APIで情報を取得する

axiosのインストール

REST APIで情報を取得するのに便利なJavaScriptのモジュールaxiosをインストールします。

$ npm install axios

コンポーネントを作成する

BlogCardコンポーネントを作成します。

useStateを利用して、postというStateに取得したデータを格納します。

src/components/iframly/BlogCard.jsx
import axios from "axios"
import React, { useEffect, useState } from "react"

const BlogCard = (props) => {
  
  const url = props.children
  const [post, setPost] = useState("")
  const apikey = "★払いだされたAPIキー★"

  const req = `https://iframe.ly/api/oembed?url=${url}&api_key=${apikey}`
  useEffect(() => {
    axios
      .get(req)
      .then((res) => {
        setPost(res.data)
      })
      .catch(() => console.log("err"))
  }, [])

  return (
    <>
        <p>{post.url}</p>
        <p>{post.title}</p>
        <p>{post.thumbnail_url}</p>
    </>
  )
}

export default BlogCard
参考資料

axiosの使い方: axiosの基本的な使い方 - Qiita

コンポーネントを呼び出す

試しに、記事のレイアウトを規定しているページから呼び出してみます。

記事の本文を返却している部分の一番上に、<BlogCard>コンポーネントを呼びだす記述を入れてみます。

src/pages/{mdx.frontmatter__slug}.js
・・・省略・・・
 return (
    <Layout location={location} sideBarContents>
        <article itemScope itemType="http://schema.org/Article">
            <BlogCard>https://www.seiburailway.jp/index.html</BlogCard>        ・・・省略・・・
        </article>
    </Layout>
  )

gatsby developで、適当な記事を確認してみると、指定したURLの情報(URL、タイトル、サムネイル画像のパス)が表示できていることがわかります。

URLの情報を取得できた

コンポーネントをMDXファイル内から呼び出す

先ほどの状態だと、記事の中に好きなURLを埋め込むことができていないので、次はMDXファイル内にURLを書くことを考えます。

MDXProvider設定

MDXファイル内で、自作コンポーネント<BlogCard>を利用できるようにする必要があります。

私はgatsby-plugin-mdxを利用しているので、{mdx.frontmatter__slug}.jsの本文レンダリング部分に<MDXProvider>を追加します。

<MDXProvider>のpropsに、利用したいコンポーネントを設定することでMDX内で利用できるようになります。

src/pages/{mdx.frontmatter__slug}.js
import { graphql } from "gatsby"
import { MDXRenderer } from "gatsby-plugin-mdx"
import { MDXProvider } from "@mdx-js/react"
import * as React from "react"
import BlogCard from "../components/iframly/BlogCard"
import Layout from "../components/layout"

const BlogPost = (props) => {
・・・省略・・・
  const shortcodes = { BlogCard }
  return (
    <Layout location={location}>
      ・・・省略・・・
      <MDXProvider components={shortcodes}>        <article itemScope itemType="http://schema.org/Article">
          <header>
              ・・・省略・・・
          </header>
          <section itemProp="articleBody">
            <MDXRenderer>{post.body}</MDXRenderer>
          </section>
        </article>
      </MDXProvider>    </Layout>
  )
}

MDXファイル内から呼び出す

適当な記事の頭で、<BlogCard>を呼び出します。

myblog/20210927/index.mdx
---
title: "npm installでのパッケージインストールが上手くいかないときにやったことメモ"
・・・省略・・・
---

<BlogCard>https://www.seiburailway.jp/</BlogCard>

## はじめに
### 今回の記事の目的
・・・省略・・・

情報を取得して表示することができました。 記事内での表示

ブログカードの形を整える

ここは個人の好みで調整すればよいと思います。

ポイントとしては

  • APIで情報が取得できない場合は、単純にURLを出すようにする
  • サムネイル画像のサイズを取得し、横長の場合は画像とタイトルを縦に並べる。そうでない場合は画像は横に並べる
  • サムネイル画像がない場合は、タイトルとディスクリプションだけ出す

という点を工夫しました。今後も改善の余地ありです。

ChakraUIを利用しています。

src/components/iframly/BlogCard.jsx
import {
  Box,
  Center,
  Flex,
  Image,
  Link,
  LinkBox,
  LinkOverlay,
  Text,
} from "@chakra-ui/react"
import axios from "axios"
import React, { useEffect, useState } from "react"

const BlogCard = (props) => {
  
  const url = props.children
  const [post, setPost] = useState("")
  const [err, setErr] = useState(false)
  const apikey = "★払いだされたAPI-key★"

  const req = `https://iframe.ly/api/oembed?url=${url}&api_key=${apikey}`
  useEffect(() => {
    axios
      .get(req)
      .then((res) => {
        setPost(res.data)
       
      })
      .catch(() => setErr(true))
  }, [])

  const ratio = post.thumbnail_width / post.thumbnail_height
//   横長かどうか
  const landscape = ratio > 1.5



  return (
    <>
      {err ? (
        <Link href={url}>{url}</Link>
      ) : (
        <LinkBox
          as="article"
          w="80%"
          borderWidth="1px"
          borderColor="#065666"
          rounded="lg"
          p={4}
          href={post.url}
          m={4}
          _hover={{ backgroundColor: "#f4f8f9" }}
        >
          {/* 横長だったらblock */}
          <Flex flexWrap="wrap" display={landscape ? "block" : "flex"}>
            {/* URLが無ければこのブロックを作らない */}
            {post.thumbnail_url ? (
              <Box flex="1" minW={0}>
                <Center>
                  <Image
                    src={post.thumbnail_url}
                    objectFit="cover"
                    maxH="150px"
                  />
                </Center>
              </Box>
            ) : (
              ""
            )}
            <Box mx={5} mt={2} flex="5" minW={0} className="nondeco-a">
              <Box overflowWrap="break-word">
                <LinkOverlay href={post.url}>
                  <Text fontSize="md" fontWeight="bold" mb={1}>
                    {post.title}
                  </Text>
                </LinkOverlay>
              </Box>
              <Box minW={0}>
                <Text fontSize="sm" mb={1} overflowWrap="break-word">
                  {post.description}
                </Text>
              </Box>
              <Box textAlign="right" minW={0}>
                <Link fontSize="xs">{post.url}</Link>
              </Box>
            </Box>
          </Flex>
        </LinkBox>
      )}
    </>
 )
}

export default BlogCard

こちらを利用すると以下のように出力されます。

  • 通常パターン
  • サムネイル画像が取得できなかったパターン
  • サムネイル画像が横に長いパターン

出力結果

ブログタイトルのみ取得する

ブログカードと全く同じやり方です。

src/components/iframly/BlogTitle.jsx
import {
  Box,
  Link,
} from "@chakra-ui/react"
import axios from "axios"
import React, { useEffect, useState } from "react"

const BlogTitle = (props) => {
  const url = props.children
  const [post, setPost] = useState("")
  const [err, setErr] = useState(false)
  const apikey = "★払いだされたAPI-key★"

  const req = `https://iframe.ly/api/oembed?url=${url}&api_key=${apikey}`
  useEffect(() => {
    axios
      .get(req)
      .then((res) => {
        setPost(res.data)
      })
      .catch(() => setErr(true))
      
  }, [])

  return (
    <Box as="span">
      {err ? (
        <Link href={url}>{url}</Link>
      ) : (
        <Link href={post.url} >{post.title}</Link>
      )}
    </Box>
  )
}

export default BlogTitle

<MDXProvider>の引数に<BlogTitle>も含める必要があります。

const shortcodes = { BlogCard , BlogTitle}

【参考】今回は採用しなかった方法

手っ取り早いけど、少し面倒な方法

Iframelyのサイトに行き、

ブログカードにしたいURLを入力してCheck itを実行すると、

Check itを実行

貼り付けるだけで使えるHTMLコードを出力してくれます。

HTMLコード出力

毎回これをやるのも大変だなと思ったので、今回採用しませんでした。

APIキーを環境変数化しておく

APIをソースコード平文に載せておくとGitHubにも公開されてしまうため、環境変数化しましょう。

.envファイルにキーを追記する

.envファイルに追記します。

.env
GATSBY_API_KEY=0000000000000000000000
※実際に払い出されたキーを設定する

環境変数に設定した値は、通常ブラウザから使うことはできません。 今回は、ブラウザ側からAPIをたたきたいため、ブラウザからも環境変数を使えるようにすることがあります。

そのためには、GATSBY_を環境変数の頭につける必要があります。

逆に、他の人に知られてはいけない変数の頭には絶対にGATSBY_をつけないようにしましょう。

.envはGitHubに上げない

.envファイルを設定したので、gatsby developなど開発環境では問題なく動作します。

ただし、.envファイルには他にも公開したくない変数が格納されているため、GitHubに上げるのは好ましくありません。

そこで.gitignore.envを追加します(最初から記載されていると思いますので、確認だけでよいと思います。

.gitignore
# dotenv environment variable files
.env*

GatsbyCloudに本番用の環境変数を設定する

本番環境で使う環境変数をGatsbyCloudに設定します。 他のホスティングサービスを使っている場合はそちらに従ってください。

GatsbyCloudに本番用の環境変数を設定する

コンポーネントから環境変数を参照する

BlogCardBlogTitleコンポーネントを書き換えます。

src/components/iframly/BlogCard.jsx
import {
  Box,
  Center,
  Flex,
  Image,
  Link,
  LinkBox,
  LinkOverlay,
  Text,
} from "@chakra-ui/react"
import axios from "axios"
import React, { useEffect, useState } from "react"

const BlogCard = (props) => {
  
  const url = props.children
  const [post, setPost] = useState("")
  const [err, setErr] = useState(false)
  const apikey=process.env.GATSBY_API_KEY
  const req = `https://iframe.ly/api/oembed?url=${url}&api_key=${apikey}`
  useEffect(() => {
    axios
      .get(req)
      .then((res) => {
        setPost(res.data)
       
      })
      .catch(() => setErr(true))
  }, [])

   // 省略
  return (
    // 省略
 )
}

export default BlogCard

これで完了です。

失敗した方法

gatsby-remark-link-unfurlというプラグインを利用しようと思い色々試しましたが、なぜか変換できず、諦めました。

参考記事

APIの利用にあたっては、こちらの記事を参考にさせていただきました。

https://zenn.dev/yuji/articles/633fa46d6cab9a

おわりに

はてなブログから移行するにあたり、それっぽいブログカードを作成しました。

問題点として、VSCodeのMDXプレビュー機能がエラーになってしまって執筆がしにくいので、時間を見つけて解消したいと思います。➡markdown用のプレビュー拡張機能 Markdown Preview Enhanced を使ったら解決しました。



個別連絡はこちらへ→Twitterお問い合わせ

プロフィール

プロフィールイメージ

はち子

事業会社のシステム部門で働きはじめて5年目の会社員。システム企画/要件定義/システムアーキテクチャ等。

Twitter→@bun_sugi

過去の記事について

はてなブログに掲載の記事(主にプログラミングメモ)についてはこちらに掲載しております。(本ブログに移行中)

タグ一覧

関連記事

Copyright© 2024, エンジニアを目指す日常ブログ

お問い合わせ|プライバシーポリシー