microCMS

microCMS + Next.jsでJamstackブログを作ってみよう

かみむら

この記事は10月1日に行われたJamstackオンラインワークショップ #4「microCMS × Next編」の内容を記事にしたものです。

microCMSとNext.jsを組み合わせて、Jamstackなブログを作成することができます。

こちらが本ワークショップのイベントページです。
Jamstackオンラインワークショップ #4「microCMS × Next編」

更新情報

  • 2021/6/22 Next.js v11にアップデートとmicrocms-js-sdk対応の記事にリライトしました。

前提

下記のバージョンで開発を行っています。バージョンの差異によって若干機能が異なる可能性があります。

  • Next 11.0.0
  • react 17.0.2
  • react-dom 17.0.2

1. Next.jsプロジェクトを用意する

まずは、Next.jsのプロジェクトを作成していきます。プロジェクトの雛形を作成するCLIがあるので、コマンドを入力して作成していきましょう。

$ npx create-next-app microcms-next-jamstack-blog

そして、作成したプロジェクトに移動して開発サーバーを立ち上げます。

$ cd microcms-next-jamstack-blog
$ npm run dev

localhost:3000にアクセスすると下記の画像のように、アプリケーションが立ち上がります。

2. microCMSの用意する

次に、microCMSでAPIを作成していきます。手順は下記に用意しました。詳細については、microCMSのドキュメントを参照してください。

アカウント登録
ログイン
サービスの作成


サービスIDは一度設定すると変更ができないので、慎重に決めましょう。

APIの作成

それでは、ブログ用のAPIを作成していきます。API名にブログ、エンドポイントにblogを入力してください。

そして、リスト形式を選択します。


タイトルと本文のフィールドを用意します。APIスキーマは以下の通りです。


コンテンツの作成

適当に内容を入力し、公開します。
コンテンツ一覧画面に戻り、画面右上のAPIプレビューをクリックします。
取得ボタンをクリックし、入力内容がAPI経由で取得できるか確認してください(レスポンスJSONが表示されます)。

3. APIキーをenvファイルで保護

microCMSではリクエストにAPIキーを含める事で特定のデータを取得できます。
このAPIキーは、GitHubのパブリックで公開されてしまうのはセキュリティー面でよくありません。
なので、envファイルなどで保護してあげましょう。
.env.development.localファイルを作成します。.localをつけるとローカル環境で使うことができます。
また、.developmentをつけると開発環境で使えます。

API_KEY=xxxxxxxxxxxx

プロジェクト内でAPIキーを参照することができます。

process.env.API_KEY

Next.jsのenvファイルの取り扱いはドキュメントを参照してください。
https://nextjs.org/docs/basic-features/environment-variables

4.microcms-js-sdkの準備

公式で提供しているmicrocms-js-sdkをインストールしましょう。microcms-js-sdkはオープンソースで公開されています。
https://github.com/microcmsio/microcms-js-sdk

$ npm install --save microcms-js-sdk

そして、libsフォルダ -> client.jsを作成してSDKの初期化を行います。service-domain api-Keyを設定してください。serviceDomainXXXX.microcms.ioの場合、XXXXの部分になります。apiKeyは環境変数を参照してください。

// libs/client.js
import { createClient } from 'microcms-js-sdk';

export const client = createClient({
  serviceDomain: 'service-domain',
  apiKey: process.env.API_KEY,
});

5. ブログの一覧を表示する

それでは、実際にmicroCMSのデータを取得したページを作成してみましょう。
Next.jsでは、pages/以下に作成したファイルに基づいて自動的にルーティングされる仕組みになっています。

  • pages/index.js → 記事一覧
  • pages/about.js → Aboutページ


pages/index.jsの内容を変更してみましょう。

// pages/index.js
import Link from "next/link";
import { client } from "../libs/client";

export default function Home({ blog }) {
  return (
    <div>
      <ul>
        {blog.map((blog) => (
          <li key={blog.id}>
            <Link href={`/blog/${blog.id}`}>
              <a>{blog.title}</a>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
}

// データをテンプレートに受け渡す部分の処理を記述します
export const getStaticProps = async () => {
  const data = await client.get({ endpoint: "blog" });

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

ここで注目してもらいたいのは、getStaticPropsを使ってmicroCMSのデータを取得しているところです。
これは、ビルド時にサーバー側で呼ばれる関数です。この部分の処理は最終的にバンドルJSに含まれません。
ビルド時にデータを取得し、静的なHTMLを出力するために必要です。


それでは、再度アプリケーションを立ち上げてみましょう。microCMSのコンテンツがリスト形式で表示できています。

もしエラーが表示される場合は再度npm run devで起動し直してみましょう。

6. ブログの詳細を表示する

次に、ブログ記事の詳細画面を作っていきます。
記事の詳細画面のような動的なページは、ファイル名に[] (ブラケット)をつけます。

  • pages/blog/[id].js → 記事の詳細


それでは、pages/ディレクトリ内にblog/ディレクトリを作成して、その中に [id].jsを作成します。

// pages/blog/[id].js
import { client } from "../../libs/client";

export default function BlogId({ blog }) {
  return (
    <main>
      <h1>{blog.title}</h1>
      <p>{blog.publishedAt}</p>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.body}`,
        }}
      />
    </main>
  );
}

// 静的生成のためのパスを指定します
export const getStaticPaths = async () => {
  const data = await client.get({ endpoint: "blog" });

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

// データをテンプレートに受け渡す部分の処理を記述します
export const getStaticProps = async (context) => {
  const id = context.params.id;
  const data = await client.get({ endpoint: "blog", contentId: id });

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

ここではgetStaticPathsという関数を確認します。
Next.js側ではブログのidを知り得ないため、事前に生成するべきHTMLのパスが分かりません。
そこでこの関数内でデータを取得し、パスを定義してあげる必要があります。
ここでのパスはmicroCMSのコンテンツIDです。

またfallbackをfalseにしています。これで、getStaticPathsで返されないパスをすべて404ページで返します。

ブラウザで確認してみましょう。詳細ページを作成することができました。

ついでに404ページも作成してみましょう。pages/以下に404.jsを作成すると、静的な404ページを作成することができます。

  • pages/404.js → 404ページ
export default function Custom404() {
  return (
    <main className="main">
      <p>ページがありません。</p>
    </main>
  );
}

パスがないURLにアクセスすると、404ページが表示されることが確認できます。

7. CSSで見た目を装飾する

microCMSのリッチエディタで記述した内容はHTML形式で取得することができます。
しかし、HTML内にclassを付与することが現状できないので、本文を囲うdivにclassを付与し、タグ指定でcssを書いていきます。

Scssで記述するために、sassをインストールします。

$ npm install --save sass

ここではグローバルレベルのスタイルと、CSS Moduleを使ったコンポーネントレベルのスタイリングを説明していきます。

グローバルスタイルは_app.jsに読み込みます。今回のプロジェクトではすでにスタイルが読み込まれてることが確認できます。

// pages/_app.js
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

そして、CSS Modulesを使ったコンポーネントのレベルのスタイリングです。
CSS Modulesを使うことによって、ローカルのCSSにスコープを効かせることができます。
ファイル名は[name].module.cssのようにmoduleをつけます。

こちらが簡単なスタイリング例です。

.text {
  color: red
}

こちらがスタイルの指定方法です。

<p className={styles.text}>これはテキストです</p>

それではスタイリングしていきましょう。
styles/Home.module.cssのファイル名をHome.module.scssに変更して、Scssに対応します。
そして、下記の内容を入力してください。

.main {
  width: 960px;
  margin: 0 auto;
}

.title {
  margin-bottom: 20px;
}

.publishedAt {
  margin-bottom: 40px;
}

.post {
  & > h1 {
    font-size: 30px;
    font-weight: bold;
    margin: 40px 0 20px;
    background-color: #eee;
    padding: 10px 20px;
    border-radius: 5px;
  }

  & > h2 {
    font-size: 24px;
    font-weight: bold;
    margin: 40px 0 16px;
    border-bottom: 1px solid #ddd;
  }

  & > p {
    line-height: 1.8;
    letter-spacing: 0.2px;
  }

  & > ol {
    list-style-type: decimal;
    list-style-position: inside;
  }
}


そして記事詳細ページはこのようにスタイルを当てていきます。/pages/blog/[id].jsを編集してください。

import { client } from '../../libs/client';
import styles from '../../styles/Home.module.scss';

export default function BlogId({ blog }) {
  return (
    <main className={styles.main}>
      <h1 className={styles.title}>{blog.title}</h1>
      <p className={styles.publishedAt}>{blog.publishedAt}</p>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.body}`,
        }}
        className={styles.post}
      />
    </main>
  );
}
// more


すると、記事詳細ページを確認するとこのような見た目になっています。

8. ビルドして静的ファイルを生成してみる

試しに一度ローカル環境でビルドしてみましょう。
ビルドするために必要な.env.localファイルを作成します。内容は.env.local.developmentと同じです。

API_KEY=xxxxxxxxxxxx

そして、ビルドしてみましょう。

$ npm run build

すると、ビルドログを確認することができます。/blog/[id] を確認すると動的なページも出力できていることが確認できます。

9. ファイルをホスティングする

Next.jsの開発元でもあるVercel社が運営しているホスティングサービスを使用します。事前にアカウント作成、またはログインを済ませておいてください。
https://vercel.com

まずは、GitHubリポジトリを作成します。

$ git init
$ git add .
$ git commit -m 'first commit'
$ git remote add origin your-repository  // 自分のリポジトリを入力
$ git push -u origin main


Vercelで先ほど作成したリポジトリと連携をしていきます。
ダッシュボード上のNew Projectをクリックします。そして、今回作成したリポジトリを選択して、Import Git Repositoryimportをクリックしてください。


GitHubにVercelがインストールされていない場合はインストールする必要があります。

自分のアカウントを選択します。

そして、今回作成したリポジトリを選択します。

インポートしたプロジェクトの情報が表示されます。

Build and Output Settings はそのままでOKです。
Environment VariablesにAPI_KEYを入れてVALUEにはmicroCMSのAPIキーを入れてAddをクリックしてください。そしてDeployをクリックしましょう。

デプロイが成功すると下記の画面に移ります。ホスティングに成功しているか確認してみましょう。

10.microCMSとVercelを連携する

現在の設定では、microCMSで記事を更新しても本番環境WEBサイトには反映されません。
なぜなら、静的なサイトでは再度ビルドを行わないと、実際の環境には反映されないからです。
なのでWebhookを利用して、記事の更新時にビルドを走らせる設定にします。

Vercelのダッシュボードで、先ほどデプロイしたプロジェクトのsettingsからGit > Deploy HooksでWebhookを作成します。Hook NameはmicroCMS(ここはお好きな名前で大丈夫です)、Git Branch Nameにはmain(またはmaster)ブランチを指定しましょう。そして、Create Hookをクリックすると、Web HookのURLが発行されます。

発行されたWebhookをmicroCMSの管理画面で設定します。先ほど作成したブログのAPI設定から、カスタム通知を選択して、WebhookのURLを入力してください。

これで、記事を更新するとVercelのビルド開始されて本番環境に内容が反映されます。

11.カテゴリーを追加する

最後に、既存のブログAPIにカテゴリーを追加してみましょう。
カテゴリーを付けるにはコンテンツ参照機能を用います。






次に、ブログの API設定 > APIスキーマ に新しくカテゴリーフィールドを追加します。
種類はコンテンツ参照で、カテゴリーAPIを指定します。



そして、上記で書いたコードを記事の詳細ページに追加してみましょう。

// pages/blog/[id].js
export default function BlogId({ blog }) {
  return (
    <main className={styles.main}>
      <h1 className={styles.title}>{blog.title}</h1>
      <p className={styles.publishedAt}>{blog.publishedAt}</p>
      <p className="category">{blog.category && `${blog.category.name}`}</p>
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.body}`,
        }}
        className={styles.post}
      />
    </main>
  );
}

おわりに

今回はmicroCMSとNext.jsを組み合わせてJamstackアーキテクチャーを利用したブログを作成しました。
また、Next.jsにおける下書きプレビューの作成方法について別記事があるので参考にしてみてください。
https://microcms.io/blog/nextjs-preview-mode/

-----

microCMSは日々改善を進めています。
ご意見・ご要望は管理画面右下のチャット、公式Twitterメールからお気軽にご連絡ください!
引き続きmicroCMSをよろしくお願いいたします!

まずは、無料で試してみましょう。

APIベースの日本製ヘッドレスCMS「microCMS」を使えば、 ものの数分でAPIの作成ができます。

microCMSを無料で始める

microCMSについてお問い合わせ

初期費用無料・14日間の無料トライアル付き。ご不明な点はお気軽にお問い合わせください。

お問い合わせ

microCMS公式アカウント

microCMSは各公式アカウントで最新情報をお届けしています。
フォローよろしくお願いします。

  • twitter
  • facebook
  • github

ABOUT ME

かみむら
フロントエンドエンジニア。テックブロガーでもあります。JAMstackアーキテクチャーやSPA(React、Vue)技術が好きです。