ファーストビュー画像
ヘッダーロゴ
ホームアイコン
>
>
【Nextjs15】microCMSとAIを使って技術ブログを作成する 前編
ブログ構築

【Nextjs15】microCMSとAIを使って技術ブログを作成する 前編

作成日2025/01/13
更新日2025/01/14
アイキャッチ
# Next.js
# microCMS

作成するもの、使用するサービス

今回はAIを使って下記のような技術ブログを作成します。


使用する主な技術・サービス、機能は以下です。

技術・サービス

  • Nextjs15 App Router (レンダリング方式: SSR)
  • shadcn/ui(UIコンポーネントライブラリ)
  • v0(AIによってデザイン、コードを自動生成してくれるサービス)
  • microCMS(APIベースの日本製ヘッドレスCMS)

ブログ機能

  • 記事一覧表示 
  • カテゴリごとの記事一覧表示
  • 検索機能
  • ページネーション

Next.js環境構築

はじめにNext.jsの環境構築を行います。
ローカルにNode.jsがインストールされている前提で進めていきます。今回はv20.10.0を使用しています。

pnpmをインストール

今回はパッケージマネージャーとしてNext.js公式が推奨しているpnpmを使っていきます。
下記のコマンドでインストールします。

$ npm install -g pnpm

Next.jsプロジェクト作成

pnpm を指定してNext.jsの新規プロジェクトを作成します。
※記事執筆時点ではNext.jsバージョン15.1.4でプロジェクトが作成されます。

$ npx create-next-app@latest --use-pnpm

いくつか質問されるので以下で回答します。

  • What is your project named? → tech-blog (プロジェクト名をtech-blogとします)
  • Would you like to use TypeScript? → Yes (TypeScriptを使用します)
  • Would you like to use ESLint? → Yes (静的解析ツールのESLintを使用します)
  • Would you like to use Tailwind CSS? → Yes (Taliwind CSSを使用します)
  • Would you like your code inside a src/ directory? → No (srcディレクトリは使用しません)
  • Would you like to use App Router? (recommended) → Yes (App Routerを使用します)
  • Would you like to use Turbopack for next dev? → Yes (Turbopackを使用します)
  • Would you like to customize the import alias (`@/*` by default)? › No (importのエイリアスはデフォルトを使用します)

最終的な設定は以下のようになります。

プロジェクに移動して、立ち上げ・ブラウザでの確認ができればプロジェクト作成は完了です。

プロジェクト移動

$ cd tech-blog

プロジェクト立ち上げ

$ pnpm run dev

http://localhost:3000にアクセス

Prettier・ESLintの設定

コードを自動整形するためのPrettierを導入します。

$ pnpm add -D prettier

PrettierESLintを併用して使えるようにするためのeslint-config-prettierと、Tailwind CSSを適切な記述、順番に修正してくれるeslint-plugin-tailwindcssをインストールします。

$ pnpm add -D eslint-config-prettier eslint-plugin-tailwindcss

eslint.config.mjsに追記します。

import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const eslintConfig = [
  ...compat.extends(
    "next/core-web-vitals",
    "next/typescript",
    "plugin:tailwindcss/recommended", // 追加
    "prettier" // 追加
  ),
];

export default eslintConfig;

ファイル保存時に自動でフォーマットされるよう設定をします。
プロジェクトルートに.vscodeフォルダを作成し、その中にsettings.jsonファイルを以下の内容で作成します。

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  }
}

vscodeの拡張機能の

をそれぞれインストールします。

追加後、vscodeを再起動または画面をリロードすることでファイル保存時に自動修正が行われるようになります。

v0でデザイン作成

デザイン生成

デザイン、コードを自動生成してくれるサービスであるv0を使ってブログのデザインを作成してきます。
今回は次のような感じで指示を出してみます。

技術ブログを作成したいです。
ページとしては以下です
- 記事一覧を表示するTOPページ
※記事の表示項目はタイトル・作成日・画像・カテゴリ
- 各記事の詳細ページ

記事はカテゴリ別に検索できるようにしたい
キーワード検索ができるフォームを表示したい
ページネーションをつけたい
かっこいい感じでお願いします

こんな感じのデザインを作成してくれました。
イメージと違う部分があれば、再度チャットで指示を出すと修正してくれます。


そのままデプロイしてくれる機能などもあるようですが、今回はmicroCMSとの接続処理なども追加が必要なのであくまでデザインを参考にする目的で利用します。

shadcn/uiをインストール

v0ではshadcn/uiというUIコンポーネントライブラリを利用しているのでインストールします。
-d オプションを付けることでデフォルト設定でインストールができます。

$ pnpm dlx shadcn@latest init -d

試しにボタンコンポーネントを利用してみます。
コンポーネントを利用するには以下のように該当のコンポーネントを追加するコマンドを実行する必要があります。

$ pnpm dlx shadcn@latest add button

追加後、app/page.tsxのデフォルトの記述を消して以下のようにボタンを表示します。

import { Button } from "@/components/ui/button";

export default function Home() {
  return <Button>ボタン</Button>;
}

プロジェクトを起動してボタンが表示されていればshadcn/uiの導入は完了です。

ヘッダーを作成

検索フォームに利用するため、shadcn/uiのフォームコンポーネントを追加します。

$ pnpm dlx shadcn@latest add input 

componentsフォルダにheaders.tsxファイルを作成します。
検索機能は実装せずにv0が作成したコードを見た目だけ実装します。

import Link from "next/link";
import { Input } from "./ui/input";
import { Button } from "./ui/button";

export default function Header() {
  return (
    <header className="border-b">
      <div className="container mx-auto flex items-center justify-between px-4 py-6">
        <Link href="/" className="text-2xl font-bold">
          Tech Blog
        </Link>
        <form className="mx-4 max-w-sm flex-1">
          <div className="flex w-full max-w-sm items-center space-x-2">
            <Input type="text" placeholder="Search articles..." />
            <Button type="submit">Search</Button>
          </div>
        </form>
      </div>
    </header>
  );
}

レイアウトファイルを修正・作成したヘッダーコンポーネントを表示してみます。

import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Header from "@/components/header"; // 追加

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        {/* ↓修正↓ */}
        <div className="min-h-screen">
          <Header />
          <main className="container mx-auto px-4 py-8">{children}</main>
        </div>
        {/* ↑修正↑ */}
      </body>
    </html>
  );
}

次のような見た目になれば一旦ヘッダーは完了です。

カテゴリボタンを作成

カテゴリで絞り込み検索をするためのカテゴリボタン一覧のコンポーネントを作成します。
後ほどmicroCMSで登録したカテゴリをAPIで取得するようにしますが、一旦直書きで仮のデータを作成して表示します。

import { Button } from "./ui/button";

export default function CategoryFilter() {
  const categories = ["Frontend", "Backend", "Data Science", "DevOps"];

  return (
    <div className="mb-8">
      <h2 className="mb-4 text-xl font-semibold">Categories</h2>
      <div className="flex flex-wrap gap-2">
        <Button>ALL</Button>
        {categories.map((category) => (
          <Button variant="outline" key={category}>
            {category}
          </Button>
        ))}
      </div>
    </div>
  );
}

トップページで表示してみます。
先ほどのshadcn/uiの表示テスト用のボタンを消してカテゴリコンポーネントを表示します。

import CategoryFilter from "@/components/category-filter";

export default function Home() {
  return (
    <div>
      <CategoryFilter />
    </div>
  );
}

次のような見た目になればカテゴリ表示は一旦完了です。

記事一覧を表示

記事のカード一覧を作成します。
カテゴリと同じく、記事の情報も後ほどmicroCMSからAPIで取得しますが、一旦仮のデータを静的に表示しておきます。

shadcn/uiのカードとバッチのコンポーネントを追加します。

$ pnpm dlx shadcn@latest add card
$ pnpm dlx shadcn@latest add badge

記事一覧コンポーネントを以下の内容で作成します。

import Link from "next/link";
import { Card, CardContent, CardFooter } from "./ui/card";
import { Badge } from "./ui/badge";

export default function ArticleList() {
  const articles = [
    {
      id: 1,
      title: "Article 1",
      category: "Frontend",
      date: "2024-12-12",
    },
    {
      id: 2,
      title: "Article 2",
      category: "Backend",
      date: "2024-12-12",
    },
    {
      id: 3,
      title: "Article 3",
      category: "Data Science",
      date: "2024-12-12",
    },
  ];
  return (
    <div className="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
      {articles.map((article) => (
        <Link href="#" key={article.id}>
          <Card className="overflow-hidden">
            <div className="relative h-48 border">Image</div>
            <CardContent className="p-4">
              <Badge>{article.category}</Badge>
              <h2 className="text-xl font-semibold">{article.title}</h2>
            </CardContent>
            <CardFooter className="text-sm text-slate-600">
              {article.date}
            </CardFooter>
          </Card>
        </Link>
      ))}
    </div>
  );
}

TOPページに追加して表示してみます。

import ArticleList from "@/components/article-list";
import CategoryFilter from "@/components/category-filter";

export default function Home() {
  return (
    <div>
      <CategoryFilter />
      <ArticleList />
    </div>
  );
}

次のように表示ができていれば完了です。

ページネーションを追加

最後にページネーションを同じように仮で表示しておきます。

ページネーションコンポーネントを作成します。

import { Button } from "./ui/button";

export default function Pagination() {
  return (
    <div className="mt-8 flex items-center justify-center space-x-2">
      <Button>1</Button>
      <Button variant="outline">2</Button>
      <Button variant="outline">3</Button>
      <Button variant="outline">4</Button>
    </div>
  );
}

TOPページに追加して表示してみます。

import ArticleList from "@/components/article-list";
import CategoryFilter from "@/components/category-filter";
import Pagination from "@/components/pagination";

export default function Home() {
  return (
    <div>
      <CategoryFilter />
      <ArticleList />
      <Pagination />
    </div>
  );
}

次のようにページネーションが表示されたら完了です。

microCMS API作成

記事の管理やAPIの利用ができるようにmicroCMSの設定をしていきます。

アカウント登録

こちらのリンクからmicroCMSのアカウント登録を行います。

サービスを作成

登録が完了すると、サービスを作成という画面が出るので一から作成するを選択します。

サービス情報を入力する画面が出るので任意のサービス名サービスIDを入力します。

サービスを作成するを押して完了です。

APIを作成する

サービス作成後、APIを作成という画面が表示されるのでブログのテンプレートを選択します。

自動でブログカテゴリというAPIとサンプルデータが作成されます。

右上のAPI設定から詳しい情報を確認できます。
ブログのAPIではタイトル、内容、アイキャッチ画像、カテゴリの4つのスキーマが作成されています。

ブログAPIスキーマのカテゴリもう一つのAPIであるカテゴリを参照しており、一つのブログ記事につき、一つのカテゴリを紐づけることができます。

テスト用のデータを追加

デフォルトで作成されているデータを削除して、テスト用のデータを追加していきます。

「コンテンツの削除」でデフォルトのデータを削除します。

「カテゴリ」はv0が作成してくれた4種類を登録します。

各カテゴリ詳細ページで「コンテンツID」という値を確認することができます。
デフォルトでは「6ggodi40g」のようにランダムな文字列になっていますが、「backend」のようにわかりやすい値に変更しておきます。

ここを変更することで後に各カテゴリごとの記事一覧ページを作成する際にパスが「categories/backend」のようになり、SEO的にも有利になります。

ブログの方もいくつかデータを作成しておきます。

これでmicroCMSの準備は完了です。

これで前編は終了です。
後編ではmicroCMSと接続して動的にデータを表示してブログを完成させます。

share on
xアイコンfacebookアイコンlineアイコン