zenn-markdown-htmlでX(twitter)やリンクカードなどの埋め込みコンテンツ(embed)が表示されない

公開日:
目次

TL;DR

  • zenn-markdown-htmlの利用方法が変更になり、埋め込みコンテンツが表示されなくなった
  • markdownToHtml関数のembedOriginに https://embed.zenn.studio を指定すると表示できる
  • 商用利用したい場合は自作する必要がある

zenn-markdown-htmlで埋め込みコンテンツ(embed)が表示されない

このブログでは、markdownパーサにzennが提供しているzenn-markdown-htmlを利用しているのですが、Twitterやリンクカードなど一部の埋め込みコンテンツが表示されない問題が発生していることに気づきました。

ちなみになぜかYoutubeの埋め込みコンテンツは問題なく表示されていました。

原因

GitHubの関連イシュー(Issue #437)を見ると、zenn-markdown-htmlの利用方法が変更になっていたようです。

以前の利用方法

zenn editorのgithubにあるREADMEを見ると、使い方として下記のように記載されています。

import markdownToHtml from 'zenn-markdown-html';

const html = markdownToHtml(markdown);

上記の方法で実装していたのですが、これだとtwitterなどの一部の埋め込みコンテンツが表示されないようです。

対処法

関連イシューから、zenn editorではなく、zenn-markdown-htmlのREADMEを参照すると対処法がわかりました。

Zennと同じ埋め込み要素を利用するには、下記のようにmarkdownToHtml関数にembedOriginを指定する必要があるようです。

import markdownToHtml from 'zenn-markdown-html';

const html = markdownToHtml(markdown, {
  embedOrigin: 'https://embed.zenn.studio',
});

zenn-markdown-htmlのembed.tsファイルを確認すると以下の要素は、Zennの場合は埋め込みサーバで提供しているようです。

  • card
  • tweet
  • gist
  • github
  • mermaid

埋め込み要素をカスタマイズしたい場合は、下記のようにcustomEmbedを指定すると色々できるみたいです。

import markdownToHtml from 'zenn-markdown-html';

const html = markdownToHtml(markdown, {
  customEmbed: {
    tweet(url) {
      return `
        <blockquote
          className="twitter-tweet"
          data-conversation="${
            src.split(/&|\?/).includes('conversation=none') ? 'none' : ''
          }"
        >
          <a href="${url}" rel="nofollow noopener noreferrer" target="_blank">
            ${url}
          </a>
        </blockquote>
      `;
    },
  },
});

商用利用したい場合

商用利用したい場合は、埋め込みサーバが使えないので自作する必要があります。

twitterの埋め込みコンテンツの場合は、下記のようにtwitterのoEmbed APIを利用して実装すると表示できました。

import markdownToHtml from 'zenn-markdown-html';

const html = markdownToHtml(markdown);

const convertTwitterLinkToEmbedHtml = async (html) => {
  const twitterLinkRegex = /<a\s+[^>]*href="https:\/\/twitter\.com\/[^"']*\/status\/(\d+)"[^>]*>https:\/\/twitter\.com\/[^<]+<\/a>\s*<a\s+[^>]*href="https:\/\/twitter\.com\/[^"']*\/status\/(\d+)"[^>]*>https:\/\/twitter\.com\/[^<]+<\/a>/g;
  const twitterLinks = html.match(twitterLinkRegex);
  for (const twitterLink of twitterLinks) {
    const twitterUrlRegex = /href="([^"]*)"/g;
    const twitterUrl = twitterUrlRegex.exec(twitterLink)[1];
    const res = await fetch(`https://publish.twitter.com/oembed?url=${twitterUrl}&omit_script=true`);
    const json = await res.json();
    html = html.replace(twitterLink, json.html);
  }
  return html;
}

const convertedHtml = await convertTwitterLinkToEmbedHtml(html)

twitterの埋め込みコンテンツは下記のスクリプトも必要なので、どこかに追加しましょう。

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Next.jsの場合

Next.jsの場合は、useEffectで追加しないと初回表示時にスクリプトが読み込まれないので注意が必要です。

参考記事の下記のコードを追加したらいけました。

useEffect(() => {
  const s = document.createElement("script");
  s.setAttribute("src", "https://platform.twitter.com/widgets.js");
  s.setAttribute("async", "true");
  document.head.appendChild(s);
}, []);

これで個人ブログでも簡単に埋め込みコンテンツが使えます。大変便利ですね。

参考