📚 初心者から実践活用まで完全網羅 — ClaudeCodeを今日から使いこなせる

実践活用術

ClaudeCodeでNext.jsアプリを作る完全ガイド【ブログサイト構築実践】

ClaudeCodeとNext.jsを組み合わせてブログサイトを構築する方法を解説します。プロジェクト作成、App Router、SSG、Markdownレンダリング、Cloudflareへのデプロイまで実践形式で学べます。

2026-05-11·約16分で読める·#ClaudeCode#Next.js#ブログ
[ Advertisement ]

ClaudeCodeでNext.jsアプリを作る完全ガイド【ブログサイト構築実践】

Next.jsは、Reactベースのフレームワークの中でも特に人気が高く、個人ブログから大規模なECサイトまで幅広く採用されています。一方で「App Routerの使い方が難しい」「サーバーコンポーネントとクライアントコンポーネントの違いが分からない」など、初心者にとってのハードルも年々上がっています。本記事ではClaudeCodeを活用して、シンプルなブログサイトを実際に構築するプロセスを通じて、Next.jsの全体像を掴めるように解説します。プロンプト例とコードサンプルを多数交えていますので、手を動かしながら読み進めてみてください。

結論:ClaudeCodeとNext.jsで作るブログは「速い・安い・運用しやすい」

ClaudeCodeを使ってNext.jsでブログサイトを作ると、以下のメリットがすぐに享受できます。第一に、開発スピードが圧倒的に速いです。プロジェクト作成からデプロイまで、慣れている人なら1〜2時間、初心者でも半日あれば公開できます。第二に、運用コストがほぼゼロです。Cloudflare Workers / Pagesを使えば、個人レベルのトラフィックでは無料枠で十分賄えます。第三に、SEOやパフォーマンスに強い設計が標準で手に入ります。SSG(静的サイト生成)、画像最適化、メタデータ管理など、本来は手作業が大変な部分をフレームワークが面倒見てくれます。

具体的には次のような機能を実装します。Markdownで記事を書き、ファイルベースで管理し、トップページに記事一覧を表示し、個別記事ページを動的に生成し、タグやカテゴリで絞り込めるようにします。デザインはTailwindCSSで整え、最終的にCloudflareにデプロイして公開します。このすべてをClaudeCodeとの対話だけで完成させられるのが、本記事のゴールです。

h2-1. Next.jsプロジェクトの作成と初期設定

まずはClaudeCodeでプロジェクトを立ち上げます。Node.js 20以上が入っていることを確認しておきましょう。

mkdir my-blog && cd my-blog
claude

ClaudeCodeのプロンプトで次のように依頼します。

プロンプト例1:「このフォルダに、Next.js 14(App Router)+ TypeScript + TailwindCSSの構成でブログサイトの土台を作成してください。記事はMarkdownファイル(content/posts配下)で管理する想定です。」

ClaudeCodeは create-next-app を実行し、必要な依存パッケージ(gray-matterremarkremark-html または next-mdx-remote)をインストールしてくれます。出来上がる初期構成は次のような形です。

my-blog/
├── app/
│   ├── layout.tsx
│   ├── page.tsx
│   └── posts/[slug]/page.tsx
├── content/
│   └── posts/
├── lib/
│   └── posts.ts
├── public/
├── next.config.mjs
├── tailwind.config.ts
└── package.json

App Routerではapp/配下のフォルダ構造がそのままURLになります。例えばapp/about/page.tsxを作れば/aboutでアクセスできます。これがPages Router時代と最大の違いです。

h2-2. Markdownで記事を管理する仕組みを作る

ブログ記事の管理方式はいくつかありますが、本記事ではローカルのMarkdownファイルを読み込む方式を採用します。理由は3つあります。1つ目は学習コストが低いこと、2つ目はGitで履歴管理できること、3つ目はサーバーやDBが不要なことです。

content/posts/hello-world.md のような形でファイルを作成し、冒頭にYAML形式のメタデータ(frontmatter)を書きます。

---
title: "はじめての投稿"
date: "2026-05-11"
description: "ブログ初投稿の記事です。"
tags: ["雑記", "はじめに"]
---

これはMarkdownで書いた本文です。**強調**や *斜体*、リスト、コードブロックも使えます。

ClaudeCodeに次のように依頼します。

プロンプト例2:「content/posts配下のMarkdownファイルを全件読み込んで、frontmatterと本文を取り出すユーティリティをlib/posts.tsに作成してください。getAllPosts と getPostBySlug の2つの関数をエクスポートしてください。」

生成されるコードはこのようなイメージです。

// lib/posts.ts
import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

const postsDirectory = path.join(process.cwd(), 'content/posts');

export type PostMeta = {
  slug: string;
  title: string;
  date: string;
  description?: string;
  tags?: string[];
};

export function getAllPosts(): PostMeta[] {
  const files = fs.readdirSync(postsDirectory).filter((f) => f.endsWith('.md'));
  return files
    .map((file) => {
      const slug = file.replace(/\.md$/, '');
      const raw = fs.readFileSync(path.join(postsDirectory, file), 'utf8');
      const { data } = matter(raw);
      return { slug, ...(data as Omit<PostMeta, 'slug'>) };
    })
    .sort((a, b) => (a.date < b.date ? 1 : -1));
}

export function getPostBySlug(slug: string) {
  const filePath = path.join(postsDirectory, `${slug}.md`);
  const raw = fs.readFileSync(filePath, 'utf8');
  const { data, content } = matter(raw);
  return { slug, meta: data as Omit<PostMeta, 'slug'>, content };
}

このユーティリティが、ブログ全体の心臓部になります。

h2-3. 記事一覧ページを実装する

トップページに記事一覧を表示します。App Routerではapp/page.tsxがトップページに対応します。

// app/page.tsx
import Link from 'next/link';
import { getAllPosts } from '@/lib/posts';

export default function HomePage() {
  const posts = getAllPosts();
  return (
    <main className="max-w-3xl mx-auto px-4 py-10">
      <h1 className="text-3xl font-bold mb-8">ブログ記事一覧</h1>
      <ul className="space-y-6">
        {posts.map((post) => (
          <li key={post.slug} className="border-b pb-4">
            <Link href={`/posts/${post.slug}`} className="block hover:opacity-70">
              <h2 className="text-xl font-semibold">{post.title}</h2>
              <p className="text-sm text-gray-500">{post.date}</p>
              {post.description && <p className="mt-2 text-gray-700">{post.description}</p>}
            </Link>
          </li>
        ))}
      </ul>
    </main>
  );
}

ポイントはgetAllPosts()をコンポーネント内で直接呼んでいる点です。これはApp Routerのサーバーコンポーネントだから可能な書き方で、fsモジュールをそのまま使えます。クライアントコンポーネントでは不可能なので、'use client'を付けないように注意しましょう。

h2-4. 個別記事ページを動的に生成する

次に個別記事ページを実装します。動的ルートはapp/posts/[slug]/page.tsxという構造で表現します。

プロンプト例3:「app/posts/[slug]/page.tsxを作成してください。Markdown本文をHTMLに変換して表示し、generateStaticParamsで全記事のslugを返してください。SEOのためにmetadataも適切に出力してください。」

生成されるコードはこちらです。

// app/posts/[slug]/page.tsx
import { getAllPosts, getPostBySlug } from '@/lib/posts';
import { remark } from 'remark';
import html from 'remark-html';
import type { Metadata } from 'next';

export async function generateStaticParams() {
  return getAllPosts().map((post) => ({ slug: post.slug }));
}

export async function generateMetadata({ params }: { params: { slug: string } }): Promise<Metadata> {
  const { meta } = getPostBySlug(params.slug);
  return {
    title: meta.title,
    description: meta.description,
  };
}

export default async function PostPage({ params }: { params: { slug: string } }) {
  const { meta, content } = getPostBySlug(params.slug);
  const processed = await remark().use(html).process(content);
  const contentHtml = processed.toString();

  return (
    <article className="max-w-3xl mx-auto px-4 py-10">
      <h1 className="text-3xl font-bold mb-2">{meta.title}</h1>
      <p className="text-sm text-gray-500 mb-8">{meta.date}</p>
      <div
        className="prose prose-lg max-w-none"
        dangerouslySetInnerHTML={{ __html: contentHtml }}
      />
    </article>
  );
}

generateStaticParamsを使うことで、ビルド時に全記事のページが静的HTMLとして書き出されます。これがSSG(Static Site Generation)です。Cloudflareにデプロイすると、各記事はCDNからミリ秒単位で配信されるようになります。

h2-5. デザインをTailwindで整える

proseクラスは@tailwindcss/typographyプラグインが提供する便利なスタイルです。導入は簡単で、ClaudeCodeに依頼すれば一発で設定してくれます。

プロンプト例4:「@tailwindcss/typographyを導入して、Markdown本文にprose-lgが適用されるようにしてください。ダークモードにも対応してください。」

tailwind.config.tsが次のように更新されます。

import type { Config } from 'tailwindcss';

const config: Config = {
  content: ['./app/**/*.{ts,tsx}', './content/**/*.md'],
  darkMode: 'media',
  theme: { extend: {} },
  plugins: [require('@tailwindcss/typography')],
};

export default config;

そしてレイアウトでもダークモード対応を入れておくと完璧です。

// app/layout.tsx
import './globals.css';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
        {children}
      </body>
    </html>
  );
}
[ Advertisement ]

h2-6. タグページとカテゴリ機能を追加する

ブログとしての完成度を上げるためにタグ機能を実装します。app/tags/[tag]/page.tsxを作って、指定タグを含む記事の一覧を表示します。

プロンプト例5:「app/tags/[tag]/page.tsxを作成し、指定したタグを含む記事一覧を表示してください。generateStaticParamsでは全タグを返してください。」

// app/tags/[tag]/page.tsx
import Link from 'next/link';
import { getAllPosts } from '@/lib/posts';

export async function generateStaticParams() {
  const tags = new Set<string>();
  getAllPosts().forEach((post) => post.tags?.forEach((t) => tags.add(t)));
  return Array.from(tags).map((tag) => ({ tag: encodeURIComponent(tag) }));
}

export default function TagPage({ params }: { params: { tag: string } }) {
  const tag = decodeURIComponent(params.tag);
  const posts = getAllPosts().filter((p) => p.tags?.includes(tag));
  return (
    <main className="max-w-3xl mx-auto px-4 py-10">
      <h1 className="text-2xl font-bold mb-6">タグ: {tag}</h1>
      <ul className="space-y-4">
        {posts.map((post) => (
          <li key={post.slug}>
            <Link href={`/posts/${post.slug}`} className="hover:underline">
              {post.title}
            </Link>
          </li>
        ))}
      </ul>
    </main>
  );
}

これでタグ単位の絞り込み一覧が完成です。記事側のタグ表示にもリンクを張れば回遊性が一気に高まります。

h2-7. 検索機能とRSSフィードの追加

ブログ運営で意外と重宝するのが検索とRSSです。検索はクライアントサイドの簡易実装で十分です。

プロンプト例6:「全記事のメタ情報をJSONとして書き出し、トップページに簡易検索ボックスを追加してください。検索はクライアント側で行い、タイトルとdescriptionの部分一致でフィルタしてください。」

検索コンポーネントは'use client'を使ったクライアントコンポーネントになります。サーバーコンポーネントから渡された記事配列をstateで保持し、入力に応じてフィルタするだけです。

RSSはapp/feed.xml/route.tsを作って動的に生成します。

// app/feed.xml/route.ts
import { getAllPosts } from '@/lib/posts';

export async function GET() {
  const posts = getAllPosts();
  const items = posts
    .map(
      (p) => `
    <item>
      <title><![CDATA[${p.title}]]></title>
      <link>https://example.com/posts/${p.slug}</link>
      <pubDate>${new Date(p.date).toUTCString()}</pubDate>
      <description><![CDATA[${p.description ?? ''}]]></description>
    </item>`
    )
    .join('');

  const xml = `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>My Blog</title>
    <link>https://example.com</link>
    <description>個人ブログのRSS</description>
    ${items}
  </channel>
</rss>`;
  return new Response(xml, { headers: { 'Content-Type': 'application/xml' } });
}

h2-8. Cloudflareへのデプロイと公開設定

完成したサイトはCloudflare Workers / Pagesにデプロイします。Next.jsをCloudflareで動かす際は、@opennextjs/cloudflareを使うのが現在の推奨です。

プロンプト例7:「Next.jsアプリをCloudflare Workersにデプロイできるように@opennextjs/cloudflareを導入してください。wrangler.jsoncを設定し、preview_urlsはfalseにしてください。」

wrangler.jsoncの重要な設定はこのようになります。

{
  "name": "my-blog",
  "main": ".open-next/worker.js",
  "compatibility_date": "2026-05-01",
  "preview_urls": false,
  "assets": {
    "directory": ".open-next/assets",
    "binding": "ASSETS"
  }
}

preview_urlsfalseにするのは、Cloudflare Accessでアクセス制御をしている場合に、プレビューURL経由で認証をバイパスされるのを防ぐためです。社内利用のブログでは特に重要なポイントです。

ビルドとデプロイは次の通りです。

npx opennextjs-cloudflare build
npx wrangler deploy

数十秒で本番環境に反映されます。独自ドメインを使いたい場合はCloudflareダッシュボードからカスタムドメインを設定すればOKです。

FAQ

Q1. なぜApp RouterでPages Routerを使わないのですか? A. Next.js 14以降の公式推奨がApp Routerだからです。サーバーコンポーネントによる転送量削減や、レイアウトのネスト構造など、新しい機能はApp Router側にのみ追加されています。

Q2. WordPressから移行する場合のコツは? A. まずエクスポートしたXMLからMarkdownへの変換スクリプトをClaudeCodeに作ってもらいましょう。画像のリンクは相対パスに置換することで管理しやすくなります。

Q3. 画像はどう管理すればよいですか? A. public/images/配下に置いて、next/imageコンポーネント経由で読み込むのが基本です。Cloudflare Imagesと連携するとさらに高速化できます。

Q4. コメント機能は付けられますか? A. Disqus、Giscus(GitHub Discussions連携)などの外部サービスを埋め込むのが簡単です。スパム対策や運用負荷の面でもおすすめです。

Q5. アクセス解析はどうすればよいですか? A. Cloudflare Web Analyticsは無料・プライバシーフレンドリーで、JavaScriptスニペットを貼るだけで使えます。

Q6. 記事数が増えてもビルドは速いままですか? A. 数百件であれば全く問題ありません。数千件規模になったらISR(Incremental Static Regeneration)の活用やオンデマンドビルドを検討しましょう。

Q7. ブログ以外にどんなサイトが作れますか? A. ドキュメントサイト、ポートフォリオ、ランディングページ、社内ナレッジベースなど、コンテンツ駆動のサイトは何でも作れます。

まとめ

ClaudeCodeとNext.jsの組み合わせは、個人開発の理想形のひとつです。Markdownで気軽に記事を書きながら、世界中に高速配信できるブログが手に入ります。本記事ではプロジェクトの作成から記事管理、SSG、タグ機能、RSS、Cloudflareへのデプロイまでを一気通貫で実践しました。重要なのは、ClaudeCodeに任せきりにせず「なぜこの設計なのか」を問いながら学ぶ姿勢です。そうすれば、ブログ運用を通じてWebアプリ開発全般のスキルが自然と身についていきます。

関連記事

[ Advertisement ]

この記事をシェア

Related Articles

あわせて読みたい記事