markdoc + Next.js製のブログでIframelyの外部リンクを埋め込むタグを作る
最近、このブログをGatsby.jsからStripeが開発しているmarkdocとNetx.js使って書き換えた。
それに伴って、外部リンクを見栄え良く表示できるIframelyを埋め込む仕組みを変更する必要があったため、markdocの独自タグを作成した。
markdocの独自タグ
markdocでは、Markdownファイルで{% TAG %}
の形式の独自タグを書くことでMarkdownでは表現できないHTML要素を記述できる。
これを実現するために、独自タグを実装する。
md形式でのコンポーネントやHUGOのショートコードのようなものである。
この仕組みを使って、Markdownファイル内に次のように記述すれば、Iframelyを使って外部サイトの情報を埋め込みできるタグを実装する。
{% iframely href="URL" id="DATA-ID" /%}
なお、記事におけるファイル構成は、Schema customizationに従うものとする。
タグの実装
今回は「iframely」という名前のタグを実装する。
STEP1:タグを定義する
「iframely.markdoc.ts」という名前のファイルを作る。
render
プロパティにはレンダリングするコンポーネント(後述)を指定し、attributes
にはそのコンポーネントに渡す引数を定義する。
Iframelyを使って外部リンクに埋め込むときは、次の2箇所が可変となるので、これらの値を引数で取る形にする。
a
要素に指定するhref
属性data-iframely-url
属性内の「//iframely.net/この部分
」、
import { Iframely } from "../../components/Iframely";
// eslint-disable-next-line import/no-anonymous-default-export
export default {
render: Iframely,
description: "iframely",
attributes: {
href: {
type: String,
description: "The link url",
},
id: {
type: String,
description: "The data-framely ID",
}
},
};
STEP2:コンポーネントを実装する
「iframely.markdoc.ts」に指定するコンポーネントを実装する。
レンダリングするHTMLはIframelyが生成するタグと同じ構造にして、可変となるhref
とid
の値をコンポーネントに渡す。
実際のIframelyが生成するタグにはclass
属性がついているが、自分のブログでは表示が壊れたためそれらのclass
属性は付与していない。 また、Iframelyを表示するためのスクリプトタグは、<head>
に適用するので削除した。
export function Iframely({ href, id }) {
return (
<>
<div>
<a
href={href}
data-iframely-url={`//iframely.net/${id}?card=small`}
></a>
</div>
</>
);
}
実装したタグをtagsのルートファイルにエクスポートする。
export { default as iframely } from "./iframely.markdoc";
STEP3:Iframelyのスクリプトタグを追加する
記事を表示するコンポーネントに、Iframelyのスクリプトタグを入れる。
今回はreact-helmetを使って<head>
タグ内に差し込む。
import React from "react";
+ import Helmet from "react-helmet";
// 省略
export function Home({ options }) {
+ React.useEffect(() => {
+ if (window.iframely) {
+ window.iframely.load();
+ }
+ });
// 省略
const { ast, content, config, errors } = useMarkdocCode(code);
const children = content.children;
return (
<>
+ <Helmet>
+ <script async src="https://cdn.iframe.ly/embed.js" />
+ </Helmet>
<!-- 省略 -->
<div>
{Markdoc.renderers.react(children, React, {
components: config.components,
})}
</div>
</>
):
}
window
にiframely
がないと怒られるので、型定義ファイルに宣言を追加する。
declare var iframely: any;
STEP4:実装したタグをmarkdown parserのスキーマに渡す
独自タグをmarkdocのschemaに渡す。
parseしたmarkdownがレンダリングされる前にタグを解釈する必要があるので、記事を表示するコンポーネント内でschemaを指定する。
Create a custom tag
次の例は、トップページでMarkdownファイルのコンテンツを表示するコードである。
import React from "react";
+ import { getSchema } from '@markdoc/next.js/runtime';
// 省略
+ import * as tags from "../markdoc/tags";
+ const schema = {
+ tags,
+ };
// 省略
export function useMarkdocCode(code) {
const ast = React.useMemo(() => Markdoc.parse(code), [code]);
const config = React.useMemo(() => {
+ const { components, ...rest } = getSchema(schema);
return {
...rest,
variables: {
markdoc: {
frontmatter
},
},
components,
};
}, [ast]);
}
export function Home({ options }) {
const { ast, content, config, errors } = useMarkdocCode(code);
const children = content.children;
return (
<>
<Helmet>
<script async src="https://cdn.iframe.ly/embed.js" />
</Helmet>
<div>
{Markdoc.renderers.react(children, React, {
components: config.components,
})}
</div>
</>
):
}
動作確認
Iframelyにアクセスする。
埋め込みたいページのURLを入力して、タグを生成する。
生成されたタグにある
data-iframely-url
属性内の「//iframely.net/この部分
」(ID)を確認する。ブログにMarkdownファイルを追加し、次のように記載する。
{% iframely href="埋め込む記事のURL" id="ID" /%}
プレビューして、リンクが埋め込まれて表示されていれば成功となる。