Next.js と TypeScript で、microCMS を導入する流れ

TwitterFacebookHatena

TL;DR

エンジニアの方であれば、自分で管理画面を作ったり、VSCode とマークダウンで記事を作成するでしょうから CMS は不要かと思います。しかし、管理画面から記事を投稿したい人や、マークダウンを扱えないクライアントさんには、CMS を使う必要があります。

この記事では、Next.js 13~ と TypeScript による microCMS の 2023 年 6 月現在の導入方法について解説します。実際のコードを交えながら、microCMS の基本的な使い方から、より高度な実装方法までをカバーします。

開発環境 バージョン
Next.js (Pages Router) 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

microCMS の基本

microCMS とは、ヘッドレス CMS(Content Management System)の一つです。ヘッドレス CMS とは、フロントエンドとバックエンドが完全に分離された CMS のことを指します。これにより、データの入力と表示が分離され、任意のフロントエンドでデータを表示することができます。

microCMS は API によるデータの取得が可能で、これにより JavaScript のフレームワークである Next.js からデータを取得し、ウェブサイト上に表示することが可能となります。

microCMS でブログを作成する

microCMS の管理画面に入ります。

  1. 管理画面の左 > コンテンツ(API)の「+」をクリック > 「カテゴリ API」を作成
  2. API 名 > 「ブログ」、エンドポイント名を「blog」と指定
  3. API スキーマ > フィールド ID > 「title」、表示名を「タイトル」
  4. API スキーマ > フィールド ID > 「content」、表示名を「内容」
  5. API スキーマ > フィールド ID > 「eyecatch」、表示名を「アイキャッチ」、種類を「画像」

そして、画面右上の API プレビューをクリックすると、情報が表示されますので、X-MICROCMS-API-KEY の右に書いてあるキーをコピーしておきます。

microCMS でカテゴリの作成

  1. 管理画面の左 > コンテンツ(API)の「+」をクリック > 「カテゴリ API」を作成
  2. API 名 > 「カテゴリ」、エンドポイント名を「 categories」と指定
  3. API スキーマ > フィールド ID > 「name」、表示名を「カテゴリ名」
  4. API スキーマ > フィールド ID > 「slug」、表示名を「スラッグ」

次にブログの API に再度移動して

  1. API スキーマのフィールド ID を「category」、表示名を「カテゴリ」、種類を「複数コンテンツ参照 - カテゴリ」

「複数コンテンツ参照」にすることで複数のカテゴリを設定できます。

これでブログとカテゴリの結びつきができました。

Next.js

Next.js をインストールします。プロジェクト名は、micro-cms とします。

npx create-next-app@latest micro-cms

microcms-js-sdk をインストールします。

npm install --save microcms-js-sdk

ルートに、lib/client.ts を作成し、次のように記述します。

import { createClient } from "microcms-js-sdk";

export const client = createClient({
  serviceDomain: process.env.SERVICE_DOMAIN || "",
  apiKey: process.env.API_KEY || "",
});

また、ルートに.env.development.local を作成し、先程コピーした microCMS の情報を書き込みます。API_KEY の横に、コピーしたキーを追加し、SERVICE_DOMAIN の横には、microCMS 管理画面の左上にあるドメインを追加します。

API_KEY=ここにX-MICROCMS-API-KEY の右に書いてあるキーを貼り付けます
SERVICE_DOMAIN=microCMSで発行されたドメイン名を貼り付けます

ビルドエラー 「check serviceDomain and apiKey」

ビルド時に以下のようなエラーがでることがあります。

info Collecting page data ...Error: parameter is required (check serviceDomain and apiKey)
    at exports.createClient

.env.development.localの設定は通常、開発時(next dev)にしか適用されません。ビルド(next build)をローカルで行っている場合、それらの環境変数が適用されない可能性があります。

対処法としては.env.local という名前の新しいファイルを作成し、.env.development.local の内容をそのままコピーします。これでビルド時にも環境変数が適用されて、ビルドエラーが解決します。

ブログ一覧ページ

pages/blog/index.tsx を作り、次のように編集します。

// src/pages/blog/index.tsx
import Link from "next/link";
import { client } from "lib/client";

type Category = {
  id: string;
  name: string;
};

type Blog = {
  id: string;
  title: string;
  category: Category[];
};

const Blog = ({ blog }: { blog: Blog[] }) => {
  return (
    <div className="content">
      {blog.map((blogItem) => (
        <div key={blogItem.id}>
          <ul>
            <li>
              {blogItem.category.map((categoryItem: Category) => (
                <Link
                  key={categoryItem.id}
                  href={`/categories/${categoryItem.id}`}
                >
                  {categoryItem.name}
                </Link>
              ))}
            </li>
          </ul>
          <h3 className="mb-10">
            <Link href={`/blog/${blogItem.id}`}>{blogItem.title}</Link>
          </h3>
        </div>
      ))}
    </div>
  );
};

export default Blog;

export const getStaticProps = async () => {
  const blogData = await client.get({ endpoint: "blog" });

  return {
    props: {
      blog: blogData.contents,
    },
  };
};

Image コンポーネントを使用する

Next.js の next/image を使用して外部の画像を最適化するには、 next.config.js ファイルにその画像のホストを設定する必要があります。これは、不適切な画像のリンクや潜在的に危険な URL を防ぐためのセキュリティ対策です。next.config.js を以下のように編集してみてください。

/** @type {import('next').NextConfig} */
module.exports = {
  reactStrictMode: true,
  images: {
    domains: ["images.microcms-assets.io"],
  },
};

詳細ページ

詳細ページを作ります。タイトル、カテゴリー、アイキャッチ画像、本文を表示します。次のコードは Next.js と microCMS を使ってブログ記事を表示するためのものです。

// src/pages/blog/[id].tsx
import { client } from "lib/client";
import Image from "next/image";
import Link from "next/link";

type Eyecatch = {
  url: string;
  height: number;
  width: number;
};

type Category = {
  id: string;
  name: string;
};

type Blog = {
  id: string;
  title: string;
  publishedAt: string;
  content: string;
  category: Category[];
  eyecatch: Eyecatch;
};

export default function BlogId({ blog }: { blog: Blog }) {
  const date = new Date(blog.publishedAt).toLocaleDateString();

  return (
    <main className="content">
      {blog.eyecatch && (
        <Image
          src={blog.eyecatch.url}
          alt="blog eyecatch"
          width={blog.eyecatch.width}
          height={blog.eyecatch.height}
        />
      )}
      <h1>{blog.title}</h1>
      <p>{date}</p>
      {blog.category &&
        blog.category.map((categoryItem) => (
          <p key={categoryItem.id}>
            <Link href={`/categories/${categoryItem.id}`}>
              {categoryItem.name}
            </Link>
          </p>
        ))}
      {blog.content && (
        <div
          dangerouslySetInnerHTML={{
            __html: `${blog.content}`,
          }}
        />
      )}
    </main>
  );
}

export const getStaticPaths = async () => {
  const data = await client.get({ endpoint: "blog" });

  const paths = data.contents.map((content: Blog) => `/blog/${content.id}`);
  return { paths, fallback: false };
};

export const getStaticProps = async (context: { params: { id: string } }) => {
  const id = context.params.id;
  const data = await client.get({ endpoint: "blog", contentId: id });
  //console.log(data) // ここで取得したデータをログ出力

  return {
    props: {
      blog: data,
    },
  };
};

それでは、それぞれの部分について詳しく見ていきましょう。

モジュールのインポートと型定義

最初の部分では、必要なモジュールをインポートしています。また、取得したブログ記事のデータに対する型を定義しています。Blog型は一つのブログ記事を表し、Eyecatch型は記事のアイキャッチ画像に関する情報を表しています。

BlogIdコンポーネント

BlogIdは React の関数コンポーネントで、ブログ記事を表示するためのものです。props としてblogを受け取り、記事のアイキャッチ画像、タイトル、日付、カテゴリー名、そして本文を表示します。本文は HTML 形式で取得されるため、dangerouslySetInnerHTML属性を使っています。

getStaticPaths関数

この関数は Next.js の静的生成(Static Generation)に必要なもので、生成すべきパスのリストを返します。ここではまずclient.getを使って全てのブログ記事を取得し、その結果から各記事に対応するパス(/blog/<記事のID>)を生成しています。

getStaticProps関数

この関数も Next.js の静的生成に必要なもので、各パスに対する props(ここではblog)を返します。ここでは引数のcontextから記事の ID を取り出し、それを使ってclient.getでその記事のデータを取得しています。

ドキュメント

以上、microCMS と Next.js を使ってブログを作成する方法を説明しました。詳しいサービスの使い方は、Next.js との連携| microCMS ドキュメントを参照してください。

Next.js と TypeScript で、microCMS を導入する流れ