はじめに
Gatsbyブログに、はてなブログのようなブログカードを挿入します。
このブログは元々はてなブログで書いていたので、同じようなことがしたかったです。
2022/9/3追記
- APIキーを環境変数化していなかったので、環境変数化しました。▶APIキーを環境変数化しておく
- 頻繁に無料枠を超えてしまい、APIが無効化されてしまうため、
<BlogTitle>
は使用をやめました。代わりに「Copy Title and Url as Markdown Style」というChrome拡張機能を使うことにしました。▶外部サイトのタイトルを1クリックで取得できるChrome拡張機能【Copy Title and Url as Markdown Style】 | エンジニアを目指す日常ブログ
完成したもの
MDXファイル内にURLを記載すると、指定したURLの情報が、Gatsbyのコンポーネント内で取得できるようになりました。
これらの情報を利用して、自分の好きなデザインのブログカードや、タイトルのみの表示を可能としました。
使うもの
IframelyのAPIを使います。
IframelyのAPIについて
まずはIframelyに無料アカウント登録します。無料で月に1,000クエリまで叩けるようです。
するとIframly登録後ページに、API keyが払いだされます。
右側にあるのが、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に取得したデータを格納します。
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>
コンポーネントを呼びだす記述を入れてみます。
・・・省略・・・
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、タイトル、サムネイル画像のパス)が表示できていることがわかります。
コンポーネントをMDXファイル内から呼び出す
先ほどの状態だと、記事の中に好きなURLを埋め込むことができていないので、次はMDXファイル内にURLを書くことを考えます。
MDXProvider設定
MDXファイル内で、自作コンポーネント<BlogCard>
を利用できるようにする必要があります。
私はgatsby-plugin-mdx
を利用しているので、{mdx.frontmatter__slug}.js
の本文レンダリング部分に<MDXProvider>
を追加します。
<MDXProvider>
のpropsに、利用したいコンポーネントを設定することでMDX内で利用できるようになります。
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>
を呼び出します。
---
title: "npm installでのパッケージインストールが上手くいかないときにやったことメモ"
・・・省略・・・
---
<BlogCard>https://www.seiburailway.jp/</BlogCard>
## はじめに
### 今回の記事の目的
・・・省略・・・
ブログカードの形を整える
ここは個人の好みで調整すればよいと思います。
ポイントとしては
- APIで情報が取得できない場合は、単純にURLを出すようにする
- サムネイル画像のサイズを取得し、横長の場合は画像とタイトルを縦に並べる。そうでない場合は画像は横に並べる
- サムネイル画像がない場合は、タイトルとディスクリプションだけ出す
という点を工夫しました。今後も改善の余地ありです。
※ChakraUIを利用しています。
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
こちらを利用すると以下のように出力されます。
- 通常パターン
- サムネイル画像が取得できなかったパターン
- サムネイル画像が横に長いパターン
ブログタイトルのみ取得する
ブログカードと全く同じやり方です。
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を実行すると、
貼り付けるだけで使えるHTMLコードを出力してくれます。
毎回これをやるのも大変だなと思ったので、今回採用しませんでした。
APIキーを環境変数化しておく
APIをソースコード平文に載せておくとGitHubにも公開されてしまうため、環境変数化しましょう。
.env
ファイルにキーを追記する
.env
ファイルに追記します。
GATSBY_API_KEY=0000000000000000000000
※実際に払い出されたキーを設定する
環境変数に設定した値は、通常ブラウザから使うことはできません。 今回は、ブラウザ側からAPIをたたきたいため、ブラウザからも環境変数を使えるようにすることがあります。
そのためには、GATSBY_
を環境変数の頭につける必要があります。
逆に、他の人に知られてはいけない変数の頭には絶対にGATSBY_
をつけないようにしましょう。
.env
はGitHubに上げない
.env
ファイルを設定したので、gatsby develop
など開発環境では問題なく動作します。
ただし、.env
ファイルには他にも公開したくない変数が格納されているため、GitHubに上げるのは好ましくありません。
そこで.gitignore
に.env
を追加します(最初から記載されていると思いますので、確認だけでよいと思います。
# dotenv environment variable files
.env*
GatsbyCloudに本番用の環境変数を設定する
本番環境で使う環境変数をGatsbyCloudに設定します。 他のホスティングサービスを使っている場合はそちらに従ってください。
コンポーネントから環境変数を参照する
BlogCard
、BlogTitle
コンポーネントを書き換えます。
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の利用にあたっては、こちらの記事を参考にさせていただきました。
おわりに
はてなブログから移行するにあたり、それっぽいブログカードを作成しました。
問題点として、VSCodeのMDXプレビュー機能がエラーになってしまって執筆がしにくいので、時間を見つけて解消したいと思います。➡markdown用のプレビュー拡張機能 Markdown Preview Enhanced を使ったら解決しました。