本ブログにて、X(旧Twitter)のリンクを埋め込みカード表示に対応させました。
これまで、Xのリンクを貼ると一般的なリンクカードとして表示されていましたが、
今回の対応により、以下のようにリッチな埋め込みカードで表示されるようになります。
「生成AIで帯広記念予想は当たるのか?」
— もみじん (@momijinn_aka) 2026年1月3日
確率最大化で1点勝負を徹底解説!
詳細は https://t.co/0rltFuJGZx #競馬AI #バンえい
対応方法
それでは、具体的な対応方法をサクッとご紹介しましょう。
前提
- Astroプロジェクトであること
コード
まず、Xの埋め込み用HTMLを取得するために、
https://publish.twitter.com/oembed のAPIを利用します。
このAPIにリクエストを送ると、埋め込み用のHTMLがJSON形式で返ってきます。
const fetchXEmbed = async (url: string): Promise<string> => {
const twitterUrl = url.replace('x.com', 'twitter.com');
try {
const res = await fetch(
`https://publish.twitter.com/oembed?url=${encodeURIComponent(twitterUrl)}&omit_script=false&lang=ja`,
);
const data = await res.json();
return data.html;
} catch (e) {
// e.g. oEmbed失敗時は別の処理へフォールバック
return '';
}
};
次に、このロジックをremarkのプラグインとして組み込みます。
MarkdownのAST(抽象構文木)を走査し、Xのリンクを検出したら、
上記の fetchXEmbed 関数を呼び出して埋め込み用のHTMLを取得。
そして、そのHTMLでMarkdown ASTをゴリゴリと書き換える、という流れです。
export default function remarkOgpLinkCard() {
return async (tree: any) => {
let transformers: any[] = [];
visit(tree, 'paragraph', (paragraphNode: any, index) => {
if (paragraphNode.children.length !== 1) {
return tree;
}
if (paragraphNode && paragraphNode.data !== undefined) {
return tree;
}
visit(paragraphNode, 'text', (textNode: any) => {
const urls = textNode.value.match(
/https?:\/\/[\w/:%#\$&\?\(\)~\.=\+\-]+/g,
);
if (urls === null) {
return;
}
const url = urls[0];
transformers = transformers.concat(async () => {
let htmlValue = '';
// Xの埋め込み用HTMLを取得
if (url.match(/^https?:\/\/(www\.)?x\.com\//)) {
htmlValue = await fetchXEmbed(url);
tree.children[index] = {
type: 'html',
value: htmlValue,
};
}
});
});
});
await Promise.all(transformers.map((transformer) => transformer()));
return tree;
};
}
最後に、この作成したプラグインを astro.config.ts に組み込めば完了です。
これで、ブログ記事内でXのリンクを貼るだけで、
自動的に埋め込みカード表示が実現できます。
import remarkOgpLinkCard from './plugins/remarkOgpLinkCard/index.js';
export default defineConfig({
// ...
markdown: {
remarkPlugins: [
// ...
remarkOgpLinkCard,
],
},
});
その他
今回はXのリンクを埋め込みカード表示に対応させましたが、
実はInstagramの埋め込み対応も検討しました。
しかし、Instagramの埋め込み用HTMLを取得するにはアクセストークンが必要でして、
その管理という名の「先送り症候群」が発症してしまい、今回は見送る形となりました。
技術的負債?いえ、これは未来への布石です!
近い将来、Instagramの埋め込みカード表示対応も実現できればと考えていますので、乞うご期待ください。