目標
- Markdownファイルを使って静的コンテンツを扱う方法を学ぶ。
- 必要なライブラリをインストールし、MarkdownをHTMLに変換する。
- ブログの投稿リストと詳細ページを作成する。
1. Markdownを使う準備
Markdownは軽量で書きやすいフォーマットで、ブログに最適です。Next.jsでMarkdownを扱うために、ライブラリを導入します。
ライブラリのインストール:
ターミナルで以下を実行:
npm install gray-matter remark remark-html
- gray-matter: Markdownのメタデータ(フロントマター)を解析。
- remark & remark-html: MarkdownをHTMLに変換。
2. Markdownファイルの作成
プロジェクトに投稿データを保存するディレクトリを作成します。
手順:
-
posts/
フォルダをプロジェクトルートに作成。 -
以下のようなMarkdownファイルを作成(例:
posts/sample-post.md
):--- title: "初めての投稿" date: "2025-03-31" --- これはNext.jsで作ったブログのサンプル投稿です。 Markdownで簡単に書けますね!
-
もう1つ追加(
posts/second-post.md
):--- title: "2つ目の投稿" date: "2025-04-01" --- 2番目の投稿です。Next.jsとMarkdownの組み合わせが最高!
3. Markdownを読み込む関数
投稿リストと詳細を取得する関数を作成します。
手順:
-
lib/posts.ts
ファイルを作成し、以下を記述:import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; import { remark } from 'remark'; import html from 'remark-html'; const postsDirectory = path.join(process.cwd(), 'posts'); // すべての投稿のメタデータを取得 export function getAllPosts() { 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 matterResult = matter(fileContents); return { id, ...(matterResult.data as { title: string; date: string }), }; }); return allPostsData.sort((a, b) => (a.date < b.date ? 1 : -1)); } // 個別の投稿データを取得 export async function getPostData(id: string) { const fullPath = path.join(postsDirectory, `${id}.md`); const fileContents = fs.readFileSync(fullPath, 'utf8'); const matterResult = matter(fileContents); const processedContent = await remark() .use(html) .process(matterResult.content); const contentHtml = processedContent.toString(); return { id, contentHtml, ...(matterResult.data as { title: string; date: string }), }; }
4. 投稿リストの表示
ホーム画面に投稿リストを表示します。
app/page.tsx
の編集:
import { getAllPosts } from '../lib/posts';
export default async function Home() {
const posts = getAllPosts();
return (
<div className="min-h-screen flex flex-col items-center bg-gray-100 py-8">
<h1 className="text-4xl font-bold text-blue-600 mb-8">ブログ投稿</h1>
<ul className="space-y-4 max-w-2xl w-full">
{posts.map((post) => (
<li key={post.id} className="bg-white p-4 rounded shadow">
<a href={`/posts/${post.id}`} className="text-xl font-semibold text-blue-500 hover:underline">
{post.title}
</a>
<p className="text-gray-500">{post.date}</p>
</li>
))}
</ul>
</div>
);
}
5. 投稿詳細ページの作成
動的ルートを使って、個別の投稿ページを作成します。
app/posts/[id]/page.tsx
の作成:
import { getPostData } from '../../../lib/posts';
export default async function Post({ params }: { params: { id: string } }) {
const postData = await getPostData(params.id);
return (
<div className="max-w-2xl mx-auto p-6">
<h1 className="text-3xl font-bold text-blue-600 mb-4">{postData.title}</h1>
<p className="text-gray-500 mb-6">{postData.date}</p>
<div
className="prose"
dangerouslySetInnerHTML={{ __html: postData.contentHtml }}
/>
</div>
);
}
注意:
-
dangerouslySetInnerHTML
を使用するので、信頼できるコンテンツのみを扱うようにしてください。
実践:シンプルなブログ
- Home: 投稿リストを表示(タイトルと日付)。
- 投稿詳細: クリックするとMarkdownの内容をHTMLとして表示。
確認:
-
npm run dev
でサーバーを起動し、http://localhost:3000
と/posts/sample-post
を確認。
エピソード5の終了
- Markdownを使って静的ブログを構築しました。
- 投稿リストと詳細ページを実装しました。
次回のエピソード:データベースを統合し、動的なブログにアップグレードします。
この記事が役に立ったと思ったら、ぜひ「いいね」を押して、ストックしていただければ嬉しいです!次回のエピソードもお楽しみに!