Maemaemae

remark-gfm でGitHub風マークダウンを実装する方法

基本セットアップ

インストール

# 基本パッケージ
npm install remark remark-gfm remark-html

# React使用時
npm install remark-react

基本設定

import { remark } from 'remark';
import remarkGfm from 'remark-gfm';
import remarkHtml from 'remark-html';

const processor = remark()
  .use(remarkGfm)
  .use(remarkHtml);

Next.js での実装

MDX プロジェクトでの設定

// next.config.js
import remarkGfm from 'remark-gfm';

const nextConfig = {
  experimental: {
    mdxRs: true,
  },
  pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
};

export default nextConfig;

ブログ記事の処理

// lib/markdown.js
import fs from 'fs';
import path from 'path';

export async function getPostContent(slug) {
  const fullPath = path.join('posts', `${slug}.md`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');

  const processedContent = await remark()
    .use(remarkGfm)
    .use(remarkHtml)
    .process(fileContents);

  return processedContent.toString();
}

React コンポーネントでの活用

マークダウンレンダラー

// components/MarkdownRenderer.jsx
import { useEffect, useState } from 'react';
import { remark } from 'remark';
import remarkGfm from 'remark-gfm';
import remarkReact from 'remark-react';

export default function MarkdownRenderer({ content }) {
  const [result, setResult] = useState(null);

  useEffect(() => {
    remark()
      .use(remarkGfm)
      .use(remarkReact)
      .process(content)
      .then((file) => setResult(file.result));
  }, [content]);

  return <div className="markdown-content">{result}</div>;
}

GFM 機能の活用例

表とタスクリスト

| 機能 | 対応状況 |
|------|----------|
| 表組み | ✅ |
| タスクリスト | ✅ |
| 削除線 | ✅ |

- [x] remark-gfm インストール
- [x] 基本設定完了
- [ ] スタイリング調整

よくあるエラーと対処法

// エラー: remark processor が未定義
// 解決: async/await の適切な使用
async function processMarkdown(content) {
  try {
    const result = await remark()
      .use(remarkGfm)
      .use(remarkHtml)
      .process(content);
    return result.toString();
  } catch (error) {
    console.error('Markdown processing failed:', error);
    return '';
  }
}

スタイリングの最適化

CSS での表スタイル

/* styles/markdown.css */
.markdown-content table {
  border-collapse: collapse;
  width: 100%;
  margin: 1rem 0;
}

.markdown-content th,
.markdown-content td {
  border: 1px solid #ddd;
  padding: 8px 12px;
  text-align: left;
}

.markdown-content th {
  background-color: #f5f5f5;
  font-weight: 600;
}

パフォーマンス最適化

メモ化とキャッシュ

// utils/markdownCache.js
const cache = new Map();

export async function processWithCache(content) {
  const hash = createHash(content);

  if (cache.has(hash)) {
    return cache.get(hash);
  }

  const result = await remark()
    .use(remarkGfm)
    .use(remarkHtml)
    .process(content);

  const output = result.toString();
  cache.set(hash, output);
  return output;
}

package.json スクリプト例

{
  "scripts": {
    "markdown:build": "node scripts/build-markdown.js",
    "markdown:watch": "chokidar 'content/**/*.md' -c 'npm run markdown:build'"
  }
}

トラブルシューティング

よくある問題

  1. 表が正しく表示されない

    • CSS の table スタイルを確認
    • markdown の表記法をチェック
  2. タスクリストがレンダリングされない

    • remark-gfm の読み込み順序を確認
    • プラグインのバージョン互換性をチェック
  3. 削除線が効かない

    • ~~ の記法が正しいか確認
    • CSS で text-decoration: line-through を設定

参考資料


remark 15.x および remark-gfm 4.x を基準としています。

remarkMarkdownNext.jsReactGFM