AstroにおけるMarkdown
Markdownは、ブログ記事やドキュメントなど、テキストを多く含むコンテンツを書くためによく使われます。Astroには、タイトル、説明、タグなどのカスタムプロパティを定義するためのフロントマターYAML(またはTOML)も含められる、Markdownファイルの組み込みサポートが備わっています。
Astroでは、GitHub Flavored Markdownでコンテンツを書き、それを.astroコンポーネント内でレンダリングできます。これにより、コンテンツ向けに設計された使い慣れた記述形式と、Astroのコンポーネント構文・アーキテクチャの柔軟性を組み合わせることができます。
コンポーネントやJSX式をMarkdownに含めるなど、追加の機能を利用したい場合は、@astrojs/mdxインテグレーションを追加して、MarkdownコンテンツをMDXで記述してください。
Markdownファイルの整理
Section titled “Markdownファイルの整理”ローカルのMarkdownファイルは、src/ディレクトリ内のどこにでも配置できます。src/pages/内に配置されたMarkdownファイルは、サイト上にMarkdownページを自動的に生成します。
Markdownのコンテンツとフロントマタープロパティは、ローカルファイルのインポートを通じて、あるいはコンテンツコレクションのヘルパー関数で取得したデータからクエリしてレンダリングする際に、コンポーネントから利用できます。
ファイルインポートとコンテンツコレクションのクエリ
Section titled “ファイルインポートとコンテンツコレクションのクエリ”ローカルのMarkdownは、単一のファイルであればimport文で、複数のファイルを一度にクエリしたい場合はViteのimport.meta.glob() (EN)を使って、.astroコンポーネントにインポートできます。Markdownファイルからエクスポートされるデータは、このようにして.astroコンポーネント内で利用できます。
関連するMarkdownファイルのグループがある場合は、コレクションとして定義する (EN)ことを検討してください。コレクションには、ファイルシステム上の任意の場所やリモートにMarkdownファイルを保存できるなど、いくつかの利点があります。
コレクションでは、ファイルインポートではなく、Markdownコンテンツのクエリとレンダリングに特化した最適化されたAPIを使用します。コレクションは、ブログ記事や製品情報など、同じ構造を共有するデータセットを対象としています。スキーマでその構造を定義すれば、バリデーション、型安全性、エディタ上のインテリセンスも得られます。
JSXのような動的な式
Section titled “JSXのような動的な式”Markdownファイルをインポートまたはクエリしたあと、フロントマターのデータや本文を含む動的なHTMLテンプレートを.astroコンポーネント内で記述できます。
---title: '史上もっとも素晴らしい投稿'author: 'Ben'---
これが私の_素晴らしい_投稿です!---import * as greatPost from './posts/great-post.md';const posts = Object.values(import.meta.glob('./posts/*.md', { eager: true }));---
<p>{greatPost.frontmatter.title}</p><p>執筆者: {greatPost.frontmatter.author}</p>
{greatPost.compiledContent()}
<p>過去の投稿:</p><ul> {posts.map(post => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}</ul>利用可能なプロパティ
Section titled “利用可能なプロパティ”コンテンツコレクションのクエリから取得したMarkdown
Section titled “コンテンツコレクションのクエリから取得したMarkdown”getCollection()やgetEntry()といったヘルパー関数でコレクションからデータを取得する場合、Markdownのフロントマタープロパティはdataオブジェクト経由で利用できます(例: post.data.title)。さらに、bodyにはコンパイルされていない生の本文コンテンツが文字列として含まれます。
render() (EN)関数は、Markdownの本文、生成された見出しのリスト、およびremarkやrehypeプラグインが適用されたあとの変更済みフロントマターオブジェクトを返します。
Markdownのインポート
Section titled “Markdownのインポート”importまたはimport.meta.glob()を使ってMarkdownをインポートした場合、.astroコンポーネント内で以下のエクスポートされたプロパティが利用できます。
file- ファイルの絶対パス(例:/home/user/projects/.../file.md)。url- ページのURL(例:/en/guides/markdown-content)。frontmatter- ファイルのYAML(またはTOML)フロントマターで指定されたデータ。<Content />- ファイルの完全なレンダリング済みコンテンツを返すコンポーネント。rawContent()- 生のMarkdownドキュメントを文字列として返す関数。compiledContent()- MarkdownドキュメントをHTML文字列にコンパイルして返す非同期関数。getHeadings()- ファイル内のすべての見出し(<h1>から<h6>)の配列を、{ depth: number; slug: string; text: string }[]型で返す非同期関数。各見出しのslugは、その見出しに対して生成されるIDに対応しており、アンカーリンクに利用できます。
たとえばMarkdownのブログ記事では、次のようなAstro.propsオブジェクトが渡されます。
Astro.props = { file: "/home/user/projects/.../file.md", url: "/en/guides/markdown-content/", frontmatter: { /** Frontmatter from a blog post */ title: "Astro 0.18 Release", date: "Tuesday, July 27 2021", author: "Matthew Phillips", description: "Astro 0.18 is our biggest release since Astro launch.", }, getHeadings: () => [ {"depth": 1, "text": "Astro 0.18 Release", "slug": "astro-018-release"}, {"depth": 2, "text": "Responsive partial hydration", "slug": "responsive-partial-hydration"} /* ... */ ], rawContent: () => "# Astro 0.18 Release\nA little over a month ago, the first public beta [...]", compiledContent: () => "<h1>Astro 0.18 Release</h1>\n<p>A little over a month ago, the first public beta [...]</p>",}<Content />コンポーネント
Section titled “<Content />コンポーネント”<Content />コンポーネントは、MarkdownファイルからContentをインポートすることで利用できます。このコンポーネントは、ファイルの本文全体をHTMLにレンダリングして返します。Contentは好みの名前にリネームすることもできます。
同様に、<Content />コンポーネントをレンダリングすることで、Markdownコレクションエントリの本文をHTMLとしてレンダリング (EN)できます。
---// import文import {Content as PromoBanner} from '../components/promoBanner.md';
// コレクションのクエリimport { getEntry, render } from 'astro:content';
const product = await getEntry('products', 'shirt');const { Content } = await render(product);---<h2>本日のキャンペーン</h2><PromoBanner />
<p>セール終了日: {product.data.saleEndDate.toDateString()}</p><Content />Markdownで見出しを書くと、ページ内の特定のセクションに直接リンクできるアンカーリンクが自動的に付与されます。
---title: コンテンツのページ---## はじめに
Markdownを書く際、同じページ内の[結論](#結論)に内部リンクできます。
## 結論
ブラウザで`https://example.com/page-1/#はじめに`を開けば、「はじめに」へ直接遷移できます。Astroは、github-sluggerに基づいて見出しのidを生成します。その他の例はgithub-sluggerのドキュメントを参照してください。
見出しIDとプラグイン
Section titled “見出しIDとプラグイン”Astroは、MarkdownおよびMDXファイル内のすべての見出し要素(<h1>から<h6>)にid属性を注入します。このデータは、インポートしたファイルのMarkdownエクスポートプロパティとして提供されるgetHeadings()ユーティリティ、またはコンテンツコレクションのクエリから返されたMarkdownを使う際のrender()関数から取得できます。
これらの見出しIDは、id属性を注入するMarkdownプロセッサのプラグイン(例: rehype-slug)でカスタマイズできます。これにより、Astroのデフォルトの代わりにカスタムIDが、HTML出力とgetHeadings()が返す項目に反映されます。
Astroは、カスタムプラグインの実行後にid属性を注入するため、プラグインによってセットされたIDは保持されます。カスタムプラグインがAstroによって注入されたIDにアクセスする必要がある場合は、Astroの見出しIDプラグインをインポートし、それに依存するプラグインより前に配置してください。
import { defineConfig } from 'astro/config';import { unified, rehypeHeadingIds } from '@astrojs/markdown-remark';import { otherPluginThatReliesOnHeadingIDs } from 'some/plugin/source';
export default defineConfig({ markdown: { processor: unified({ rehypePlugins: [ rehypeHeadingIds, otherPluginThatReliesOnHeadingIDs, ], }), },});import { defineConfig } from 'astro/config';import { satteri, satteriHeadingIdsPlugin } from '@astrojs/markdown-satteri';import { otherPluginThatReliesOnHeadingIDs } from 'some/plugin/source';
export default defineConfig({ markdown: { processor: satteri({ hastPlugins: [ satteriHeadingIdsPlugin(), otherPluginThatReliesOnHeadingIDs, ], }), },});Markdownプラグイン
Section titled “Markdownプラグイン”Astroは、設定可能なMarkdownプロセッサを使ってMarkdownをレンダリングします。デフォルトでは、活発なプラグインエコシステムをもつremarkとrehypeのunifiedパイプラインを使用します。
Astroは、GitHub Flavored MarkdownとSmartyPantsを自動的に適用します。これにより、テキストからクリック可能なリンクを生成したり、引用符やemダッシュをフォーマットしたりといった便利な機能が利用できます。
プラグインを追加してMarkdownの処理を拡張したり、追加のパーサー機能を有効にしたり、まったく別のプロセッサに切り替えることもできます。Markdownの設定オプション (EN)の一覧も参照してください。
Markdownプロセッサの選択
Section titled “Markdownプロセッサの選択”markdown.processorオプション (EN)で、.mdおよび.mdxファイルをレンダリングするエンジンを制御します。Astroは公式に2つのオプションを提供しています。
unified()(デフォルト): remarkとrehypeのパイプラインで、大規模なプラグインエコシステムをもちます。satteri(): ネイティブのSätteriパイプラインで、独自のプラグインモデルを備えたより高速なMarkdownおよびMDXコンパイラです。@astrojs/markdown-satteriから提供され、別途インストールする必要があります。
remarkおよびrehypeプラグインの利用
Section titled “remarkおよびrehypeプラグインの利用”デフォルトのunified()プロセッサは、サードパーティのremarkおよびrehypeプラグインを受け付けます。これらのプラグインを使うと、目次の自動生成、アクセシブルな絵文字ラベルの適用、Markdownのスタイリング (EN)など、新しい機能でMarkdownを拡張できます。
人気のプラグインを探すには、awesome-remarkとawesome-rehypeを見てみることをおすすめします。具体的なインストール手順は、各プラグインのREADMEを参照してください。
@astrojs/markdown-remarkからunifiedをインポートし、markdown.processorを通じてプラグインを渡します。次の例では、Markdownファイルにremark-tocとrehype-accessible-emojisを適用しています。
import { defineConfig } from 'astro/config';import { unified } from '@astrojs/markdown-remark';import remarkToc from 'remark-toc';import { rehypeAccessibleEmojis } from 'rehype-accessible-emojis';
export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [[remarkToc, { heading: 'toc', maxDepth: 3 }]], rehypePlugins: [rehypeAccessibleEmojis], }), },});remarkおよびrehypeプラグインのカスタマイズ
Section titled “remarkおよびrehypeプラグインのカスタマイズ”プラグインをカスタマイズするには、ネストした配列の中で、プラグインの後にオプションオブジェクトを渡します。
以下の例では、目次の配置場所を変えるためにremarkTocプラグインにheadingオプションを追加し、見出しテキストの後ろにアンカータグを追加するためにrehype-autolink-headingsプラグインにbehaviorオプションを追加しています。
import { defineConfig } from 'astro/config';import { unified } from '@astrojs/markdown-remark';import remarkToc from 'remark-toc';import rehypeSlug from 'rehype-slug';import rehypeAutolinkHeadings from 'rehype-autolink-headings';
export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [[remarkToc, { heading: 'contents' }]], rehypePlugins: [rehypeSlug, [rehypeAutolinkHeadings, { behavior: 'append' }]], }), },});Sätteriプロセッサへの切り替え
Section titled “Sätteriプロセッサへの切り替え”remarkとrehypeの代わりにSätteriを使うには、@astrojs/markdown-satteriをインストールし、satteriをインポートしてmarkdown.processorに渡します。
import { defineConfig } from 'astro/config';import { satteri } from '@astrojs/markdown-satteri';import { myMdastPlugin } from './my-satteri-plugin.mjs';
export default defineConfig({ markdown: { processor: satteri({ mdastPlugins: [myMdastPlugin()], features: { directive: true, definitionList: true }, }), },});Sätteriプロセッサは、mdastPluginsとhastPluginsを通じて独自のプラグインを受け付け、featuresを通じて任意のパーサー機能の有効化を切り替えます。利用可能なプラグインと機能については、Sätteriのドキュメントを参照してください。
プロセッサを切り替えると、.mdと.mdxの両方のファイルでremarkとrehypeが置き換えられます。設定にあるremarkまたはrehypeのプラグインは適用されなくなります。.mdxファイルにのみSätteriを使いたい場合は、代わりにMDXインテグレーションのprocessorオプションを設定してください。
プログラムによるフロントマターの変更
Section titled “プログラムによるフロントマターの変更”remark/rehypeプロセッサを使用している場合、remarkまたはrehypeプラグインを使って、すべてのMarkdownおよびMDXファイルにフロントマタープロパティを追加できます。
-
プラグインの
file引数のdata.astro.frontmatterプロパティに、customPropertyを追加します。example-remark-plugin.mjs export function exampleRemarkPlugin() {// remarkおよびrehypeプラグインはすべて別の関数を返すreturn function (tree, file) {file.data.astro.frontmatter.customProperty = 'Generated property';}}追加:astro@2.0.0data.astro.frontmatterには、対象のMarkdownまたはMDXドキュメントのすべてのプロパティが含まれます。これにより、既存のフロントマタープロパティを変更したり、既存のフロントマターから新しいプロパティを計算したりできます。 -
このプラグインを
markdownまたはmdxインテグレーションの設定に適用します。astro.config.mjs import { defineConfig } from 'astro/config';import { unified } from '@astrojs/markdown-remark';import { exampleRemarkPlugin } from './example-remark-plugin.mjs';export default defineConfig({markdown: {processor: unified({ remarkPlugins: [exampleRemarkPlugin] }),},});または
astro.config.mjs import { defineConfig } from 'astro/config';import mdx from '@astrojs/mdx';import { unified } from '@astrojs/markdown-remark';import { exampleRemarkPlugin } from './example-remark-plugin.mjs';export default defineConfig({integrations: [mdx({processor: unified({ remarkPlugins: [exampleRemarkPlugin] }),}),],});
これで、すべてのMarkdownまたはMDXファイルのフロントマターにcustomPropertyが含まれるようになり、Markdownのインポート時やレイアウトのAstro.props.frontmatterプロパティから利用できるようになります。
MDXからMarkdown設定を拡張する
Section titled “MDXからMarkdown設定を拡張する”AstroのMDXインテグレーションは、デフォルトでプロジェクトの既存のMarkdown設定 (EN)を拡張し、Markdownプロセッサもその対象となります。個別のオプションを上書きするには、MDXの設定で同等のオプションを指定します。
次の例では、.mdxファイルに対して.mdファイルとは異なるシンタックスハイライターと異なるプラグインのセットを使用しています。
import { defineConfig } from 'astro/config';import { unified } from '@astrojs/markdown-remark';import mdx from '@astrojs/mdx';
export default defineConfig({ markdown: { syntaxHighlight: 'prism', processor: unified({ remarkPlugins: [remarkPlugin1] }), }, integrations: [ mdx({ // `.mdx`ファイルでは、`markdown.syntaxHighlight`がこのオプションで上書きされる syntaxHighlight: 'shiki',
// `.mdx`ファイルでは、`markdown.processor`が異なるremarkプラグインで上書きされる processor: unified({ remarkPlugins: [remarkPlugin2] }), }) ]});MDXからMarkdown設定を拡張したくない場合は、extendMarkdownConfigオプション(デフォルトで有効)をfalseに設定します。
import { defineConfig } from 'astro/config';import { unified } from '@astrojs/markdown-remark';import mdx from '@astrojs/mdx';
export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [remarkPlugin] }), }, integrations: [ mdx({ // Markdown設定は無視される extendMarkdownConfig: false, // プラグインなしのデフォルト`unified()`プロセッサが使用される }) ]});個別のMarkdownページ
Section titled “個別のMarkdownページ”コンテンツコレクション (EN)とMarkdownを.astroコンポーネントにインポートする方法は、Markdownをレンダリングするためのより多くの機能を提供しており、ほとんどのコンテンツを扱う際に推奨されるアプローチです。とはいえ、src/pages/にファイルを追加するだけでシンプルなページを自動的に作成できる手軽さが欲しい場合もあるでしょう。
Astroは、/src/pages/ディレクトリ内のサポートされているすべてのファイルをページとして扱い、.mdやその他のMarkdownファイルタイプも対象に含まれます。
このディレクトリやそのサブディレクトリにファイルを配置すると、ファイルのパス名を使ったページルートが自動的に構築され、HTMLにレンダリングされたMarkdownコンテンツが表示されます。非ASCIIコンテンツを書きやすくするため、Astroはページに<meta charset="utf-8">タグを自動的に追加します。
---title: Hello, World---
# こんにちは!
このMarkdownファイルは、`your-domain.com/page-1/`にページを作成します。
スタイルはほとんど当たっていないかもしれませんが、Markdownは以下のような記法をサポートしています。- **太字**と_イタリック_- リスト- [リンク](https://astro.build)- <p>HTML要素</p>- などなど!フロントマターのlayoutプロパティ
Section titled “フロントマターのlayoutプロパティ”個別のMarkdownページは機能が限られているため、Astroはそれを補う特別なフロントマタープロパティとしてlayoutを提供しています。これはAstroのMarkdownレイアウトコンポーネントへの相対パスです。layoutは、コンテンツコレクション (EN)を使ってMarkdownコンテンツをクエリ・レンダリングする際の特別なプロパティではなく、本来の用途以外でのサポートは保証されません。
Markdownファイルがsrc/pages/内にある場合は、レイアウトコンポーネントを作成し、このlayoutプロパティに指定することで、Markdownコンテンツの周りにページのシェルを与えられます。
---layout: ../../layouts/BlogPostLayout.astrotitle: Astroの概要author: Himanshudescription: Astroが素晴らしい理由を見つけよう!---これはMarkdownで書かれた投稿です。このレイアウトコンポーネントは通常のAstroコンポーネントですが、AstroテンプレートのAstro.propsを通じて特定のプロパティが自動的に利用可能になります。たとえば、MarkdownファイルのフロントマタープロパティにはAstro.props.frontmatterからアクセスできます。
---const {frontmatter} = Astro.props;---<html> <head> <!-- ... --> <meta charset="utf-8"> // デフォルトでは追加されなくなる </head> <!-- ... --> <h1>{frontmatter.title}</h1> <h2>投稿者: {frontmatter.author}</h2> <p>{frontmatter.description}</p> <slot /> <!-- ここにMarkdownコンテンツが挿入される --> <!-- ... --></html>フロントマターのlayoutプロパティを使用する場合、Astroは<meta charset="utf-8">タグを自動的に追加しなくなるため、レイアウト内に自分で含める必要があります。また、レイアウトコンポーネント内でMarkdownにスタイルを当てる (EN)こともできます。
リモートのMarkdownの取得
Section titled “リモートのMarkdownの取得”Astro内部のMarkdownプロセッサは、リモートのMarkdownを処理するためには利用できません。
コンテンツコレクション (EN)で使うためにリモートのMarkdownを取得するには、renderMarkdown()関数 (EN)にアクセスできるカスタムローダーを作成 (EN)します。
リモートのMarkdownを直接取得してHTMLにレンダリングするには、NPMから自分でMarkdownパーサーをインストールして設定する必要があります。この場合、Astro組み込みのMarkdown設定は引き継がれません。
プロジェクトに実装する前にこうした制約を理解し、代わりにコンテンツコレクションのローダーを使ってリモートのMarkdownを取得できないか検討してください。
---// 例: リモートAPIからMarkdownを取得し、// 実行時にHTMLへレンダリングする。// "marked"(https://github.com/markedjs/marked)を使用import { marked } from 'marked';const response = await fetch('https://raw.githubusercontent.com/wiki/adam-p/markdown-here/Markdown-Cheatsheet.md');const markdown = await response.text();const content = marked.parse(markdown);---<article set:html={content} />