コンテンツにスキップ

AstroにおけるMarkdown

Markdownは、ブログ記事やドキュメントなど、テキストを多く含むコンテンツを書くためによく使われます。Astroには、タイトル、説明、タグなどのカスタムプロパティを定義するためのフロントマターYAML(またはTOML)も含められる、Markdownファイルの組み込みサポートが備わっています。

Astroでは、GitHub Flavored Markdownでコンテンツを書き、それを.astroコンポーネント内でレンダリングできます。これにより、コンテンツ向けに設計された使い慣れた記述形式と、Astroのコンポーネント構文・アーキテクチャの柔軟性を組み合わせることができます。

ローカルのMarkdownファイルは、src/ディレクトリ内のどこにでも配置できます。src/pages/内に配置されたMarkdownファイルは、サイト上にMarkdownページを自動的に生成します。

Markdownのコンテンツとフロントマタープロパティは、ローカルファイルのインポートを通じて、あるいはコンテンツコレクションのヘルパー関数で取得したデータからクエリしてレンダリングする際に、コンポーネントから利用できます。

ファイルインポートとコンテンツコレクションのクエリ

Section titled “ファイルインポートとコンテンツコレクションのクエリ”

ローカルのMarkdownは、単一のファイルであればimport文で、複数のファイルを一度にクエリしたい場合はViteのimport.meta.glob() (EN)を使って、.astroコンポーネントにインポートできます。Markdownファイルからエクスポートされるデータは、このようにして.astroコンポーネント内で利用できます。

関連するMarkdownファイルのグループがある場合は、コレクションとして定義する (EN)ことを検討してください。コレクションには、ファイルシステム上の任意の場所やリモートにMarkdownファイルを保存できるなど、いくつかの利点があります。

コレクションでは、ファイルインポートではなく、Markdownコンテンツのクエリとレンダリングに特化した最適化されたAPIを使用します。コレクションは、ブログ記事や製品情報など、同じ構造を共有するデータセットを対象としています。スキーマでその構造を定義すれば、バリデーション、型安全性、エディタ上のインテリセンスも得られます。

ファイルインポートの代わりにコンテンツコレクションを使うべきタイミング (EN)について詳しく見る。

Markdownファイルをインポートまたはクエリしたあと、フロントマターのデータや本文を含む動的なHTMLテンプレートを.astroコンポーネント内で記述できます。

src/pages/posts/great-post.md
---
title: '史上もっとも素晴らしい投稿'
author: 'Ben'
---
これが私の_素晴らしい_投稿です!
src/pages/my-posts.astro
---
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>

コンテンツコレクションのクエリから取得したMarkdown

Section titled “コンテンツコレクションのクエリから取得したMarkdown”

getCollection()getEntry()といったヘルパー関数でコレクションからデータを取得する場合、Markdownのフロントマタープロパティはdataオブジェクト経由で利用できます(例: post.data.title)。さらに、bodyにはコンパイルされていない生の本文コンテンツが文字列として含まれます。

render() (EN)関数は、Markdownの本文、生成された見出しのリスト、およびremarkやrehypeプラグインが適用されたあとの変更済みフロントマターオブジェクトを返します。

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 />コンポーネントは、MarkdownファイルからContentをインポートすることで利用できます。このコンポーネントは、ファイルの本文全体をHTMLにレンダリングして返します。Contentは好みの名前にリネームすることもできます。

同様に、<Content />コンポーネントをレンダリングすることで、Markdownコレクションエントリの本文をHTMLとしてレンダリング (EN)できます。

src/pages/content.astro
---
// 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で見出しを書くと、ページ内の特定のセクションに直接リンクできるアンカーリンクが自動的に付与されます。

src/pages/page-1.md
---
title: コンテンツのページ
---
## はじめに
Markdownを書く際、同じページ内の[結論](#結論)に内部リンクできます。
## 結論
ブラウザで`https://example.com/page-1/#はじめに`を開けば、「はじめに」へ直接遷移できます。

Astroは、github-sluggerに基づいて見出しのidを生成します。その他の例はgithub-sluggerのドキュメントを参照してください。

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プラグインをインポートし、それに依存するプラグインより前に配置してください。

astro.config.mjs
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,
],
}),
},
});

Astroは、設定可能なMarkdownプロセッサを使ってMarkdownをレンダリングします。デフォルトでは、活発なプラグインエコシステムをもつremarkrehypeunifiedパイプラインを使用します。

Astroは、GitHub Flavored MarkdownSmartyPantsを自動的に適用します。これにより、テキストからクリック可能なリンクを生成したり、引用符やemダッシュをフォーマットしたりといった便利な機能が利用できます。

プラグインを追加してMarkdownの処理を拡張したり、追加のパーサー機能を有効にしたり、まったく別のプロセッサに切り替えることもできます。Markdownの設定オプション (EN)の一覧も参照してください。

markdown.processorオプション (EN)で、.mdおよび.mdxファイルをレンダリングするエンジンを制御します。Astroは公式に2つのオプションを提供しています。

  • unified()(デフォルト): remarkrehypeのパイプラインで、大規模なプラグインエコシステムをもちます。
  • satteri(): ネイティブのSätteriパイプラインで、独自のプラグインモデルを備えたより高速なMarkdownおよびMDXコンパイラです。@astrojs/markdown-satteriから提供され、別途インストールする必要があります。

remarkおよびrehypeプラグインの利用

Section titled “remarkおよびrehypeプラグインの利用”

デフォルトのunified()プロセッサは、サードパーティのremarkおよびrehypeプラグインを受け付けます。これらのプラグインを使うと、目次の自動生成アクセシブルな絵文字ラベルの適用Markdownのスタイリング (EN)など、新しい機能でMarkdownを拡張できます。

人気のプラグインを探すには、awesome-remarkawesome-rehypeを見てみることをおすすめします。具体的なインストール手順は、各プラグインのREADMEを参照してください。

@astrojs/markdown-remarkからunifiedをインポートし、markdown.processorを通じてプラグインを渡します。次の例では、Markdownファイルにremark-tocrehype-accessible-emojisを適用しています。

astro.config.mjs
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オプションを追加しています。

astro.config.mjs
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' }]],
}),
},
});

remarkとrehypeの代わりにSätteriを使うには、@astrojs/markdown-satteriをインストールし、satteriをインポートしてmarkdown.processorに渡します。

astro.config.mjs
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プロセッサは、mdastPluginshastPluginsを通じて独自のプラグインを受け付け、featuresを通じて任意のパーサー機能の有効化を切り替えます。利用可能なプラグインと機能については、Sätteriのドキュメントを参照してください。

プロセッサを切り替えると、.md.mdxの両方のファイルでremarkとrehypeが置き換えられます。設定にあるremarkまたはrehypeのプラグインは適用されなくなります。.mdxファイルにのみSätteriを使いたい場合は、代わりにMDXインテグレーションのprocessorオプションを設定してください。

プログラムによるフロントマターの変更

Section titled “プログラムによるフロントマターの変更”

remark/rehypeプロセッサを使用している場合、remarkまたはrehypeプラグインを使って、すべてのMarkdownおよびMDXファイルにフロントマタープロパティを追加できます。

  1. プラグインの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';
    }
    }
  2. このプラグインを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プロパティから利用できるようになります。

関連レシピ: Add reading time (EN)

AstroのMDXインテグレーションは、デフォルトでプロジェクトの既存のMarkdown設定 (EN)を拡張し、Markdownプロセッサもその対象となります。個別のオプションを上書きするには、MDXの設定で同等のオプションを指定します。

次の例では、.mdxファイルに対して.mdファイルとは異なるシンタックスハイライターと異なるプラグインのセットを使用しています。

astro.config.mjs
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に設定します。

astro.config.mjs
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()`プロセッサが使用される
})
]
});

Astroは、/src/pages/ディレクトリ内のサポートされているすべてのファイルをページとして扱い、.mdやその他のMarkdownファイルタイプも対象に含まれます。

このディレクトリやそのサブディレクトリにファイルを配置すると、ファイルのパス名を使ったページルートが自動的に構築され、HTMLにレンダリングされたMarkdownコンテンツが表示されます。非ASCIIコンテンツを書きやすくするため、Astroはページに<meta charset="utf-8">タグを自動的に追加します。

src/pages/page-1.md
---
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コンテンツの周りにページのシェルを与えられます。

src/pages/posts/post-1.md
---
layout: ../../layouts/BlogPostLayout.astro
title: Astroの概要
author: Himanshu
description: Astroが素晴らしい理由を見つけよう!
---
これはMarkdownで書かれた投稿です。

このレイアウトコンポーネントは通常のAstroコンポーネントですが、AstroテンプレートのAstro.propsを通じて特定のプロパティが自動的に利用可能になります。たとえば、MarkdownファイルのフロントマタープロパティにはAstro.props.frontmatterからアクセスできます。

src/layouts/BlogPostLayout.astro
---
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レイアウトについて詳しく学ぶ。

Astro内部のMarkdownプロセッサは、リモートのMarkdownを処理するためには利用できません。

コンテンツコレクション (EN)で使うためにリモートのMarkdownを取得するには、renderMarkdown()関数 (EN)にアクセスできるカスタムローダーを作成 (EN)します。

リモートのMarkdownを直接取得してHTMLにレンダリングするには、NPMから自分でMarkdownパーサーをインストールして設定する必要があります。この場合、Astro組み込みのMarkdown設定は引き継がれません。

プロジェクトに実装する前にこうした制約を理解し、代わりにコンテンツコレクションのローダーを使ってリモートのMarkdownを取得できないか検討してください。

src/pages/remote-example.astro
---
// 例: リモート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} />
貢献する コミュニティ スポンサー