はじめに
みなさんは、、React(Vite環境)でローカルのMarkdownファイルを表示させたいと思ったことはありますか?
本記事では、React(Vite環境)でローカルのMarkdownファイルをWebサイト上に表示する仕組みを解説します。
ポートフォリオサイトや技術ブログなど、静的なコンテンツ管理にMarkdownを活用したい方の参考になれば幸いです。
ディレクトリ構成例
まず、プロジェクトのディレクトリ構成は項のようになっています。
project/
└ src/
├ component/
├ contents/
│ └ markdown/
│ ├ sample/
│ │ └ sample.md
│ └ sampleHoge/
│ └ sampleHoge.md
├ utils/
│ └ Markdown.ts
└ ...
-
src/contents/markdown/
配下にMarkdownファイルを配置しています。 - Markdownのパースや表示用のユーティリティは
src/utils/Markdown.ts
で管理しています。
Markdownの読み込みとパース
1. Markdownファイルのインポート
import.meta.glob
で指定パス配下のファイルを一括でimportししています。
また、as: 'raw'
で、文字列として読み込むでいます。
const markdownFiles = import.meta.glob('/src/contents/markdown/**/*.md', {
eager: true,
as: 'raw',
})
上記のコードでは、/src/contents/markdown/
配下のmdファイルを文字列として取得している。
2. Markdownのパース
MarkdownをHTMLに変換するには、marked
や matter
などのライブラリを利用します。
利用ライブラリー
-
gray-matter
- Markdownファイルの先頭に記述する「フロントマター(YAML形式のメタデータ)」をパースするためのライブラリ
- 記事タイトルや日付、タグなどの情報を簡単に抽出できる
-
rehype-highlight
- コードブロックにシンタックスハイライト(構文強調表示)を適用するrehypeプラグイン
-
rehype-slug
- Markdown内の見出し(h1, h2, ...)に自動的にid属性(スラッグ)を付与するrehypeプラグイン
- 目次生成やアンカーリンクに便利
-
rehype-stringify
- rehypeのHTML ASTを最終的なHTML文字列に変換するプラグイン
-
remark
- Markdownをパース・変換するためのプラグインベースのフレームワーク
-
remark-gfm
- GitHub Flavored Markdown をサポートするためのremarkプラグイン
- チェックボックスやテーブル、打ち消し線など、GitHubで使われる拡張記法
-
remark-parse
- remarkのコアパーサーで、Markdownテキストを抽象構文木(AST)に変換している
-
remark-rehype
- remark(Markdown AST)からrehype(HTML AST)への変換を行うプラグイン
ユーティリティの実装例
export const getMarkdownBody = (slug: string) => {
const filePath = `/src/contents/markdown/${slug}/${slug}.md`
const markdown = markdownFiles[filePath] as string
const { content } = matter(markdown)
const parseContent = remark()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype, { allowDangerousHtml: true })
.use(rehypeSlug, { prefix: '', uniqueSlugStartIndex: 1 })
.use(rehypeHighlight)
.use(rehypeStringify, { allowDangerousHtml: true })
.process(content)
return parseContent
}
このユーティリティ関数 getMarkdownBody
は、指定したスラッグ(記事IDなど)に対応するMarkdownファイルを読み込み、フロントマターを分離した上で、Markdown本文をHTMLに変換して返す処理を行っています。
主な処理の流れは以下の通りです。
- ファイルパスの生成とMarkdown取得
- URLのslugからMarkdownファイルのパスを作成し、
import.meta.glob
で取得したファイル内容を文字列として取得する
-
フロントマターの分離
-
gray-matter
でMarkdownのメタデータと本文を分離する
-
-
MarkdownのパースとHTML変換
-
remark
を中心に各種プラグインを組み合わせてMarkdownをHTMLに変換する -
remarkParse
:MarkdownをASTに変換 -
remarkGfm
:GFM拡張記法に対応 -
remarkRehype
:Markdown AST→HTML AST変換 -
rehypeSlug
:見出しにid属性を自動付与 -
rehypeHighlight
:コードブロックにシンタックスハイライト -
rehypeStringify
:HTML ASTをHTML文字列に変換
-
-
HTMLをReturnする
- 変換後のHTMLを返すことで、Reactコンポーネントなどでそのまま表示
Markdownの表示コンポーネント
export const MarkdownBody = () => {
const { productId } = useParams()
const contents = getMarkdownBody(productId ? productId : '')
const [markdownBody, setMarkdownBody] = useState<string>()
useEffect(() => {
contents.then(content => {
setMarkdownBody(content.value as string)
})
},[])
return (
<article
className="mt-10 markdown-body"
dangerouslySetInnerHTML={{ __html: markdownBody ?? "" }}
/>
)
}
getMarkdownBody
で取得したHTMLを Articleタグの dangerouslySetInnerHTML
でHTMLを挿入しています。
まとめ
今回はじめて、Markdownをパースして、Webサイトに表示するしてみました。
正直、詳しいことはまだわかっていませんが、一旦、この方法で実装することができたため、まとめてみました。
間違っているところがあれば、教えていただけると嬉しいです。
最後まで読んでくださってありがとうございます!
普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。