Next.js と TypeScript で、Markdown を HTML に変換する

TwitterFacebookHatena

TL;DR

このページでは、Markdown を HTML に変換する方法について詳しく解説しますね。一言でいうと、Markdown は軽量マークアップ言語の一つで、HTML に変換することでウェブページ上での見た目を整えることができます。

開発環境 バージョン
Next.js 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

Markdown とは?

Markdown とは、軽量マークアップ言語の一つで、シンプルな記述法で書かれた文章を HTML に変換することができます。これにより、ウェブ上での文章の見た目を整えることができます。

Markdown は次のような形式で書かれます。

# 見出し 1

## 見出し 2

### 見出し 3

- リスト 1
- リスト 2
- リスト 3

**太字**

_斜体_

これを HTML に変換すると次のようになります。

<h1>見出し1</h1>
<h2>見出し2</h2>
<h3>見出し3</h3>

<ul>
  <li>リスト1</li>
  <li>リスト2</li>
  <li>リスト3</li>
</ul>

<b>太字</b>

<i>斜体</i>

Next.js では、サーバーサイドで Markdown を HTML に変換し、それをブラウザに送信して表示することができます。

Next.js で、Markdown を HTML に変換

dataディレクトリにある Markdown ファイルを HTML に変換するには、fspathモジュールを使ってファイルを読み込み、その内容をmarkedを使って HTML に変換します。以下にその方法を示します。

まず、Markdown ファイルを読み込み HTML に変換するユーティリティ関数を作ります。

utils/markdownToHtml.ts

import fs from 'fs'
import path from 'path'
import marked from 'marked'

export const markdownFileToHtml = (filePath: string): string => {
  const markdown = fs.readFileSync(path.resolve(filePath), 'utf-8')
  const html = marked(markdown)
  return html
}

次に、サーバーサイドレンダリングを利用して、Markdown ファイルのパスを受け取り、その内容を HTML に変換して表示する Next.js のページを作成します。

pages/markdown.tsx

import { GetServerSideProps } from 'next'
import { css } from '@emotion/react'
import { markdownFileToHtml } from '../utils/markdownToHtml'
import { markdownStyles } from '../styles/MarkdownStyles'

type Props = {
  html: string
}

const MarkdownPage = ({ html }: Props) => {
  return <div css={markdownStyles} dangerouslySetInnerHTML={{ __html: html }} />
}

export default MarkdownPage

export const getServerSideProps: GetServerSideProps<Props> = async () => {
  // 'data' ディレクトリ内の Markdown ファイルのパスを指定
  const markdownFilePath = path.join(process.cwd(), 'data', 'your-file.md')

  const html = markdownFileToHtml(markdownFilePath)

  return { props: { html } }
}

このコードでは、getServerSidePropsdataディレクトリ内の Markdown ファイルを読み込み、markedを使って HTML に変換しています。そしてその HTML をプロップとしてMarkdownPageに渡し、ブラウザにレンダリングしています。

your-file.mdは実際の Markdown ファイルの名前に置き換えてください。

この方法を使えば、dataディレクトリ内の任意の Markdown ファイルを HTML として表示できます。

Markdown ファイルの一覧を表示

data ディレクトリ内の Markdown ファイルの一覧を表示するには、fspathモジュールを使ってファイルを読み込み、その内容をgray-matterを使ってメタデータを抽出します。以下にその方法を示します。

Markdown ファイルの一覧とそのコンテンツの一部を表示するための Next.js ページを作成します。gray-matter ライブラリを使用して Markdown ファイルからメタデータを抽出します。ここでは、メタデータにタイトルと説明を含むことを想定しています。

まず、必要なパッケージをインストールします。

npm install gray-matter

次に、全ての Markdown ファイルを読み込み、メタデータを取得するユーティリティ関数を作成します。

utils/getAllPostsData.ts

import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'

export const getAllPostsData = () => {
  const postsDirectory = path.join(process.cwd(), 'data')
  const fileNames = fs.readdirSync(postsDirectory)
  const allPostsData = fileNames.map((fileName) => {
    const id = fileName.replace(/\.md$/, '')
    const fullPath = path.join(postsDirectory, fileName)
    const fileContents = fs.readFileSync(fullPath, 'utf8')
    const { data } = matter(fileContents)
    return {
      id,
      ...(data as { title: string; description: string }),
    }
  })
  return allPostsData
}

それでは、この関数を使って Next.js のページを作成します。

pages/index.tsx

import Link from 'next/link'
import { GetStaticProps } from 'next'
import { css } from '@emotion/react'
import { getAllPostsData } from '../utils/getAllPostsData'

type Post = {
  id: string
  title: string
  description: string
}

type Props = {
  posts: Post[]
}

const IndexPage = ({ posts }: Props) => (
  <div>
    {posts.map(({ id, title, description }) => (
      <div key={id}>
        <h2>
          <Link href={`/posts/${id}`}>{title}</Link>
        </h2>
        <p>{description}</p>
      </div>
    ))}
  </div>
)

export default IndexPage

export const getStaticProps: GetStaticProps<Props> = async () => {
  const posts = getAllPostsData()
  return {
    props: {
      posts,
    },
  }
}

このコードでは、getStaticProps 関数で data ディレクトリ内の全ての Markdown ファイルを読み込み、それぞれのファイルからメタデータ(タイトルと説明)を取得します。そしてそのメタデータをプロップとして IndexPage コンポーネントに渡し、各記事のタイトル(リンク付き)と説明を表示します。

注意: Markdown ファイルのメタデータ部分には titledescription を含める必要があります。この部分は YAML 形式で記述し、Markdown の本文から --- で区切ります。

data/1.md

---
title: 'タイトル'
description: '説明'
---

智に働けば角が立つ。情に棹させば流される。意地を通せば窮屈だ。
とかくに人の世は住みにくい。住みにくさが高じると、
安い所へ引き越したくなる。
どこへ越しても住みにくいと悟った時、詩が生れて、画が出来る。

この構成を使えば、data ディレクトリ内の全ての Markdown ファイルの一覧と各ファイルの説明を一覧表示することができます。

状況に応じて、Markdown を html に変換したり、TypeScript の型エラーを修正しながら、コードをカスタマイズしてください。

Next.js と TypeScript で、Markdown を HTML に変換する