5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React+ViteでMarkdownファイルをWebサイトに表示する方法

Posted at

はじめに

みなさんは、、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に変換するには、markedmatter などのライブラリを利用します。

利用ライブラリー

  • 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に変換して返す処理を行っています。

主な処理の流れは以下の通りです。

  1. ファイルパスの生成とMarkdown取得
  • URLのslugからMarkdownファイルのパスを作成し、import.meta.glob で取得したファイル内容を文字列として取得する
  1. フロントマターの分離
    • gray-matter でMarkdownのメタデータと本文を分離する
  2. MarkdownのパースとHTML変換
    • remark を中心に各種プラグインを組み合わせてMarkdownをHTMLに変換する
    • remarkParse:MarkdownをASTに変換
    • remarkGfm:GFM拡張記法に対応
    • remarkRehype:Markdown AST→HTML AST変換
    • rehypeSlug:見出しにid属性を自動付与
    • rehypeHighlight:コードブロックにシンタックスハイライト
    • rehypeStringify:HTML ASTをHTML文字列に変換
  3. 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)のフォローをお願いします。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?