2
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?

Next.js App Router + microCMS で個人技術ブログを公開しました【Atomic Design + RSS + sitemap】

2
Last updated at Posted at 2026-03-15

はじめに

個人技術ブログ FumiBlog を公開しました。

リポジトリ

以前Qiita記事の投稿を始める前に中途半端に作成していたブログを作り直しました。
今回はシンプルな技術スタックを選んで実装しています。この記事では構成や実装のポイントをまとめます。


技術スタック

役割 技術
フロントエンド Next.js 15 (App Router)
スタイリング Tailwind CSS v4
コンポーネント管理 Storybook + Atomic Design
コンテンツ管理 microCMS
ホスティング Vercel

コンポーネント設計:Atomic Design

コンポーネントの整理に Atomic Design を採用しました。

src/components/
├── atoms/       # Button, TagBadge, SourceBadge, Logo など
├── molecules/   # ArticleCard, FeaturedCard, CategoryFilter など
└── organisms/   # Header, Footer, HeroSection, BootScreen など

分類の基準

  • atoms - それ単体で意味が完結する最小単位。Props
    で見た目を制御するのみで、外部状態に依存しない
  • molecules - atoms
    を組み合わせた再利用可能なパーツ。「記事カード」「カテゴリフィルター」など、ある程度まとまった
    UI のかたまり
  • organisms - ページを構成する大きなブロック。Header や
    HeroSection のように、複数の molecules を束ねた単位

この基準を決めておくと「このコンポーネントどこに置く?」という迷いがなくなりました。

Storybook との組み合わせ

F632BBD0-065A-49B4-A817-51A5E96D7FD2.jpeg

各コンポーネントに .stories.tsx を用意し、Storybook で独立して確認できるようにしました。Props のバリエーション(サイズ・テーマ・状態)をひと目で確認できるため、デザインの一貫性を保ちやすくなります。


Next.js App Router の活用

ページ構成

src/app/
├── page.tsx              # トップページ
├── blog/
│   ├── page.tsx          # 記事一覧
│   └── [slug]/page.tsx   # 記事詳細
├── category/[slug]/      # カテゴリ別一覧
├── tag/[slug]/           # タグ別一覧
├── about/                # About ページ
├── contact/              # Contact ページ
├── sitemap.ts            # 自動サイトマップ
└── feed.xml/route.ts     # RSS フィード

App Router
のファイルベースルーティングにより、ディレクトリ構成がそのままサイトマップになります。generateStaticParams で動的ルートも静的生成できます。

OGP / generateMetadata

全ページに metadata / generateMetadata を実装しました。記事ページでは microCMS のアイキャッチ画像を OG 画像として設定しています。

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const article = await getBlogBySlug((await params).slug);
  return {
    title: article.title,
    description: article.description,
    openGraph: {
      title: article.title,
      description: article.description,
      type: 'article',
      images: article.eyecatch ? [{ url: article.eyecatch.url }] : [],
    },
  };
}

sitemap.ts

app/sitemap.tsMetadataRoute.Sitemap 型の配列を返す関数を置くだけで /sitemap.xml が自動生成されます。

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const articles = await getBlogs({ limit: 200 });
  return [
    { url: `${siteUrl}/`, changeFrequency: 'daily', priority: 1.0 },
    ...articles.map((a) => ({
      url: `${siteUrl}/blog/${a.slug}`,
      lastModified: new Date(a.publishDate),
      priority: 0.8,
    })),
  ];
}

RSS フィード

app/feed.xml/route.ts で RSS 2.0 フィードを配信しています。Route Handlers で Content-Type: application/xml を返すだけなので実装はシンプルです。


microCMS との連携

microCMS の導入手順・型定義・データ取得パターンについては以前の記事で詳しく解説しています。

今回は microCMS で書いた記事に加えて、Qiita・Zenn の外部記事も自動取得してブログ上に表示する仕組みを追加しました。

各ソースを統一の Article 型に変換する transformer パターン を採用しており、microCMS・Qiita・Zenn のどの記事も同じコンポーネントで表示できます。

type Article = {
  id: string;
  title: string;
  slug: string;
  publishDate: string;
  source: 'microcms' | 'qiita' | 'zenn';
};

記事一覧ページでは SourceBadge コンポーネントで投稿元を視覚的に区別しています。


Vercel へのデプロイ

GitHub リポジトリと Vercel を連携させ、main ブランチへのプッシュで自動デプロイされる構成にしました。

設定として必要だった環境変数は以下の 4 つです。

変数名 用途
MICROCMS_SERVICE_DOMAIN microCMS のサービスドメイン
MICROCMS_API_KEY microCMS の API キー
QIITA_USERNAME Qiita 記事取得用のユーザー名
NEXT_PUBLIC_SITE_URL OGP・sitemap 用のサイト URL

Vercel の管理画面の Settings → Environment Variables
に設定するだけで、あとはデプロイ時に自動で読み込まれます。


おわりに

Next.js App Router と microCMS の組み合わせは、個人ブログにちょうどいい規模感でした。
記事の投稿・管理は microCMS の管理画面から行えるため、ローカル環境を立ち上げることなく運用できます。

このブログでは、Qiita に書くような専門的な技術記事だけでなく、
開発メモや調べたことの整理など、もう少し気軽に書ける内容も発信していく予定です。

2
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
2
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?