O hirunewani blog

AstroでMarkdownのレンダリングにカスタムコンポーネントを利用する

Created at

AstroではMarkdownファイルにMDXやMarkdocと同様のAPIが利用できないため、Gatsybyなどの同様にremarkなどのpluginを利用する必要がある。

MDXのカスタムコンポーネント

Astroでは、MDX用にカスタムコンポーネントを利用できる機能が提供されている。

---
import Heading from "../Heading.astro";
---

<Content components={{ h1: Heading }} />

https://docs.astro.build/en/guides/markdown-content/#custom-components-with-imported-mdx

Markdown用には同様の機能がない

利用方法がほとんど同じだが、マークダウンには同様の機能がない。拡張子をmdxに変更すれば、そのまま利用することは出来るが、それが望ましいとは限らない。

https://github.com/withastro/roadmap/discussions/769

https://github.com/withastro/roadmap/discussions/423

公式ドキュメントにはMDXでは出来るとは書かれているが、Markdownでは出来ないとは書かれていないため、誤認したと思われる人による報告が見られる。

Remark pluginを利用する

GatsbyやNext.jsなどの他のフレームワークと同様に、remark pluginを利用して要素をカスタマイズすることは出来る。 一応Astro公式でも、カスタムコンポーネントを使う文脈ではないがremark pluginを利用する方法は紹介されている。

https://docs.astro.build/recipes/reading-time/

Astroのサンプルは、frontmatterを拡張するものだが、要素の変換も次のように行うことが出来る。

import { visit } from "unist-util-visit";
import { toString } from "mdast-util-to-string";

const remarkPluginSample = () => {
  const transformer = (tree: any) => {
    visit(tree, node => {
      if (node.type !== "heading") return;
      let { depth } = node;
      if (depth !== 1) return;

      let text = toString(node);
      const html = `<h1 style="color: coral">${text}</h1>`;

      node.type = "html";
      node.children = undefined;
      node.value = html;
    });
  };

  return transformer;
};

export default defineConfig({
  markdown: {
    remarkPlugins: [remarkPluginSample],
  },
});

余談:公式MDX Integrationの拡張を考える

@astrojs/mdxのコードをざっくり見ると、内部APIのaddPageExtension.mdも追加すれば対応できるように思える。試してみて問題がなさそうであれば、PRを送ってみてもいいかもしれない。

個人的にはMarkdownの場合は既存のエコシステムを使うで十分だと思うので、今回は試さなかった。