1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

2025年人気No.1のNext.jsで、最新ツールとウェブを作る! | エピソード5 Markdownでブログを構築

Posted at

目標

  • 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ファイルの作成

プロジェクトに投稿データを保存するディレクトリを作成します。

手順:

  1. posts/フォルダをプロジェクトルートに作成。

  2. 以下のようなMarkdownファイルを作成(例:posts/sample-post.md):

    ---
    title: "初めての投稿"
    date: "2025-03-31"
    ---
    これはNext.jsで作ったブログのサンプル投稿です。  
    Markdownで簡単に書けますね!
    
  3. もう1つ追加(posts/second-post.md):

    ---
    title: "2つ目の投稿"
    date: "2025-04-01"
    ---
    2番目の投稿です。Next.jsとMarkdownの組み合わせが最高!
    

3. Markdownを読み込む関数

投稿リストと詳細を取得する関数を作成します。

手順:

  1. 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を使って静的ブログを構築しました。
  • 投稿リストと詳細ページを実装しました。

次回のエピソード:データベースを統合し、動的なブログにアップグレードします。


この記事が役に立ったと思ったら、ぜひ「いいね」を押して、ストックしていただければ嬉しいです!次回のエピソードもお楽しみに!

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?