microCMS × Next.js（TypeScript）で個人ブログを作る

## 概要

`microCMS`、`Next.js（TypeScript）`を使ってブログサイトを作成した手順と、追加で実装した機能について、またその過程で学んだことの備忘録記事です。

基本的には公式のチュートリアル通りに進めていますが、TypeScriptの型付けや記事内のコードブロックのハイライトなど詰まった箇所もあったので、まとめておきたいと思います。

- デプロイURL

https://microcms-blog-hinakonagao.vercel.app/

- GitHub

https://github.com/hinakonagao/microcms_blog



※この記事ではスタイリングについては解説しません。
MaterialUIを主に使用してスタイリングを行っているので、詳しいコードはGitHubをご覧いただければと思います。

### 使用技術

- Next.js　12.0.10
- React　17.0.2
- TypeScript　4.5.5
- ESLint　8.8.0
- prettier　2.5.1

## 全体の流れ

1. microCMSの準備（アカウント作成・記事コンテンツのAPI作成）
2. Next.jsのプロジェクト作成
3. APIリクエストのための環境変数の設定
4. 公式のSDK「microcms-js-sdk」の導入
5. 記事一覧画面の作成
6. 記事詳細画面の作成
7. コードブロックのシンタックスハイライトの実装
8. 【おまけ】タグでの絞り込み機能の実装
9. 【おまけ】ページネーション機能の実装

というような流れで説明していきます。

## 1. microCMSの準備

まずはmicroCMSのアカウントを作成し、APIを作成します。
手順は公式のチュートリアルに詳しく書かれているので、ここでの説明は割愛します。
[microCMS + Next.jsでJamstackブログを作ってみよう　2. microCMSの用意する](https://blog.microcms.io/microcms-next-jamstack-blog/#:~:text=%E3%81%8C%E7%AB%8B%E3%81%A1%E4%B8%8A%E3%81%8C%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82-,2.%20microCMS%E3%81%AE%E7%94%A8%E6%84%8F%E3%81%99%E3%82%8B,-%E6%AC%A1%E3%81%AB%E3%80%81microCMS%E3%81%A7)

私は `blog` と `tag` の2つのAPIエンドポイントを作成しました。

### blog

ブログコンテンツを登録しておくリスト形式の API です。
![blog.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/a685a0c0-58b1-d5ad-d3d8-90699fb2033d.jpeg)

- title：記事タイトル
- body：記事本文
- tags：タグ（別のエンドポイント `/tag` に登録したタグを複数参照できるように設定）
- image：表示する画像名（プロジェクトディレクトリに保存した画像の中から、ブラウザに表示する画像を指定するために使用）


### tag

ブログ記事に紐づけるタグを登録しておくリスト形式の API です。
![tag.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/08e83a65-4213-0bc2-e8a6-b70d178db201.jpeg)

## 2. Next.jsプロジェクトを作成する

下記のコマンドを実行してNext.jsのプロジェクトを作成し、続けて開発サーバーを立ち上げます。

```
$ yarn create next-app --typescript
// プロジェクト名の入力を求められるので入力する

$ cd .\microcms_blog\

$ yarn dev
```

次に`src`ディレクトリを作成して、`pages`ディレクトリと`styles`ディレクトリを`src`ディレクトリの配下に移動させます。

```
$ mkdir src && mv pages src && mv styles src
```

さらに、モジュールのインポートを絶対パスで指定できるよう、ベースURLを `src` ディレクトリに設定します。

```
// tsconfig.json
{
  "compilerOptions": {
    // 追加
    "baseUrl": "src"
  }
}
```

ベースURLの設定についてはこちらの記事を参考にさせていただきました。

https://fwywd.com/tech/next-base-url


## ESLint / Prettier

ESLintとPrettierについては、こちらの記事を参考にさせていただきました。

https://fwywd.com/tech/next-eslint-prettier

### ESLint
Next.js のバージョン11 からは、デフォルトでESLintが搭載されています。

ESLintに関するインストール済のパッケージは以下の2つです。

```json
// package.json
{
  ...
  "devDependencies": {
    "eslint": "8.8.0",    // 構文解析のエンジン
    "eslint-config-next": "12.0.10",    // ESLintのルール
  }
  ...
}
```

<br>

Next.js の新規プロジェクト作成時に生成された `.eslintrc` は ESLint の設定ファイルを意味しており、**デフォルトで `eslint-config-next` の設定が適用**されています。
※`eslint-config-next`のソースコード：[next.js/index.js at canary · vercel/next.js | GitHub](https://github.com/vercel/next.js/blob/canary/packages/eslint-config-next/index.js)

デフォルトで設定されているルールの内容等については、この記事を読ませていただきました。

https://zenn.dev/thiragi/articles/555a644b35ebc1

```
// .eslintrc.json

{
  "extends": "next/core-web-vitals",    // デフォルトの設定ファイル
}
```

もしESLintで独自のルールを記述したい場合は、`rules`に記述します。

```
// .eslintrc.json

{
  "extends": "next/core-web-vitals",    // デフォルトの設定ファイル
  "rules": {
    // ルールを記述
  }
}
```

現在 `$ yarn lint` コマンドを実行すると構文解析を行うことができる状態ですが、VSCode上でリアルタイムに構文解析を行ってくれる拡張機能をインストールしておきます。

https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint

また以下の拡張機能は、画像のようにエラーの波線にマウスをホバーしなくてもエラー内容を表示してくれて便利なのでオススメです。
※ESLintに限らずTypeScriptのエラーなども同様に表示してくれます。
![error_lens.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/58b7de11-b794-30ee-dc94-8644c4911cc3.jpeg)


https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens


<br>

 `lint` コマンドの対象ディレクトリを、`src`ディレクトリ配下のファイルとなるように設定します。

```json
// package.json

"scripts": {
    "lint": "next lint --dir src"
},
```

### Prettier

Prettierはデフォルトでは含まれていないため、インストールします。

```bash
$ yarn -DE add prettier eslint-config-prettier
```

Prettierの拡張機能もインストールします。

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

Prettierのフォーマットの設定を記述します。

```
// package.json
"prettier": {
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true,
  "jsxSingleQuote": true,
  "printWidth": 100
},
```

この設定内容は、以下の記事を参照させていただきました。

https://fwywd.com/tech/next-eslint-prettier#:~:text=%E3%82%B3%E3%83%BC%E3%83%89%E3%83%99%E3%83%BC%E3%82%B9%E3%81%A7%20Prettier%20%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89%E6%95%B4%E5%BD%A2%E3%81%AE%E3%83%AB%E3%83%BC%E3%83%AB%E3%82%92%E8%A8%98%E8%BF%B0

`ESLint` と `Prettier` のコード整形がバッティングしないようにするため、以下を追記します。

```
// .eslintrc
{
  // "prettier"を追記
  "extends": ["next", "next/core-web-vitals", "prettier"]
}
```

VSCodeでのファイル保存時に、自動でPrettierによるコード整形が実行されるように設定します。

```
// .vscode/settings.json
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll": true
  }
}
```

これで`ESLint`と`Prettier`の設定は完了です。

## 3. 環境変数の設定

`microCMS`のAPIへアクセスする際は、リクエスト先の`サービスドメイン`を指定し、リクエストにそのサービスドメインの`APIキー`を含める事でデータを取得することができます。

※microCMSでのAPIキーの作成方法は公式ページを参照ください。
[APIキー（X-MICROCMS-API-KEY）](https://document.microcms.io/content-api/x-microcms-api-key)

<br>
まずは環境変数を管理するファイルを作成します。

※ `.local`をつけるとローカル環境で使うことができ、 `.development`をつけると開発環境で使えます。

```
$ touch .env.development.local
```

`.env.development.local`ファイルを作成したら、`サービスドメイン`とmicroCMSの`APIキー`を書き込みます。

※サービスドメインは、例えば自分のmicroCMSページのURLが `https://abc.microcms.io/` であれば `abc` の部分になります。

```
SERVICE_DOMAIN=xxxxxxxxxxx
API_KEY=xxxxxxxxxxxx
```

 `env` ファイルに書いた値は、以下のようにしてプロジェクト内で参照することができます。

```
process.env.API_KEY
```

## 4.microcms-js-sdkの準備

APIリクエストには公式が提供している`microcms-js-sdk`を使います。

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

```
$ yarn add microcms-js-sdk
```

 `libs/client.ts` を作成してSDKの初期化を行います。

```
$ mkdir src/libs
$ touch ./src/libs/client.ts
```

 `serviceDomain` と `apiKey` の値は `env` ファイルを参照します。

```jsx
// libs/client.ts

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

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

※`serviceDomain` と `apiKey` は `string`型、.envファイルから参照する環境変数は `string | undefined`型なので、もし以下のように `|| ""` の部分を書かないとエラーが出ます。

```jsx
serviceDomain: process.env.SERVICE_DOMAIN
// 型 'string | undefined' を型 'string' に割り当てることはできません。型 'undefined' を型 'string' に割り当てることはできません。
```

※Javascript の` a || b `は、`a`を評価してそれが`true`（相当）なら `a`、そうでないなら `b`を返します。

## 5. microCMSから記事データを取得する（一覧画面）

### 型の事前準備

microCMSから取得するブログ記事とタグのデータ型を定義しておきます。

型定義ファイルを別途作成します。

```
$ mkdir src/types
$ touch ./src/types/blog.ts
```

作成したファイルに型を定義します。

今回私が作成したAPIの場合は以下の通りになります。

```jsx
// src/types/blog.ts

export type Blog = {
  id: string;
  body: string;
  title: string;
  tags: Tag[];
  image: string;
  createdAt: string;
  updatedAt: string;
  publishedAt: string;
  revisedAt: string;
};

export type Tag = {
  id: string;
  tag: string;
  createdAt: string;
  updatedAt: string;
  publishedAt: string;
  revisedAt: string;
};
```

### APIリクエストを行う

`getStaticProps`を使ってmicroCMSのAPIを叩き、データを取得します。

 `pages/index.tsx` に以下のように書きます。

```jsx
// pages/index.js


import Link from "next/link";
import type { InferGetStaticPropsType, NextPage } from "next";
import { client } from "libs/client";    // srcから見た絶対パスで指定
import type { Blog, Tag } from "types/blog";    // srcから見た絶対パスで指定

// microCMSへAPIリクエスト
export const getStaticProps = async () => {
  const blog = await client.get({ endpoint: "blog" });
  const tag = await client.get({ endpoint: "tag" });

  return {
    props: {
      blogs: blog.contents,
      tags: tag.contents,
    },
  };
};

// Props（blogsとtags）の型
type Props = {
  blogs: Blog[];
  tags: Tag[];
};

const Home: NextPage<InferGetStaticPropsType<typeof getStaticProps>> = ({
  blogs,
  tags,
}: Props) => {
  console.log(blogs);
  console.log(tags);
  // ... 続く
```

ここで `http://localhost:3000` にアクセスすると、コンソールにAPIから取得したデータが表示されるはずです。

私の作成したAPIだと、以下の画像のようなデータが取れています。

### blogs

![blogs.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/be18eaf6-9697-96b9-2fc9-ad7c45d610bf.jpeg)


### tags

![tags.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/aca84987-afee-4804-f54b-37f5e71d655e.jpeg)

ではこのデータを画面に表示します。

※ディレクトリ構成は以下のようにします。

- `pages/index.tsx` → 記事一覧画面
- `pages/blog/[id].tsx` → 記事詳細画面

一覧画面に記事タイトルをリスト形式で表示し、記事詳細画面へのリンクをつけます。

```jsx
// pages/index.tsx

const Home: NextPage<InferGetStaticPropsType<typeof getStaticProps>> = ({
  blogs,
  tags,
}: Props) => {
  return (
    <div>
      <ul>
        {blogs.map((blog) => (
          <li key={blog.id}>
            <Link href={`/blog/${blog.id}`}>
              <a>{blog.title}</a>
            </Link>
          </li>
        ))}
      </ul>
    </div>
  )
}
```

これで `http://localhost:3000` にアクセスすると記事タイトル一覧が表示され、タイトルをクリックすると `http://localhost:3000/blog/[microCMSで設定したコンテンツID]` のURLへ遷移するはずです。

次にこの遷移先のページを作ります。

## 6. 記事詳細画面を作成する

一覧画面でブログ記事の詳細画面を作っていきます。

上記のリンク先に指定した通り、  `pages/blog/[id].ts`に詳細画面を作ります。

まずはファイルを作成します。

```
$ mkdir src/pages/blog
$ touch ./src/pages/blog/[id].tsx
```

作成したファイルを、以下のように編集します。

```jsx
// pages/blog/[id].tsx

import {
  GetStaticPaths,
  GetStaticProps,
  InferGetStaticPropsType,
  NextPage,
} from "next";
import { client } from "libs/client";
import type { Blog } from "types/blog";

// APIリクエストを行うパスを指定
export const getStaticPaths: GetStaticPaths<Params> = async () => {
  const data = await client.get({ endpoint: "blog" });

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

// microCMSへAPIリクエスト
export const getStaticProps: GetStaticProps<Props, Params> = async (
  const id = context.params?.id;
  const data = await client.get({ endpoint: "blog", contentId: id });

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

// Props（blog）の型
type Props = {
  blog: Blog;
};

const BlogId: NextPage<InferGetStaticPropsType<typeof getStaticProps>> = ({
  blog,
}: Props) => {
  return (
    <main>
      <h1>{blog.title}</h1>
      <p>{blog.publishedAt}</p>
			{blog.tags.map((tag) => (
        <li key={tag.id}>
          #{tag.tag}
        </li>
      ))}
      <div
        dangerouslySetInnerHTML={{
          __html: `${blog.body}`,
        }}
      />
    </main>
  );
}
```

### ポイント解説

- **`fallback: false` の設定**

 `getStaticPaths` で `return { fallback: false };` と書いています。

これにより、 `http://localhost:3000/blog/[存在しないコンテンツID]` のURLにアクセスすると404ページへ遷移するようになります。

※ `fallback: true` にして存在しないURLにアクセスした場合はエラー画面がブラウザに表示されてしまいます。

- **`dangerouslySetInnerHTML`**

以下の通り、記事本文は `dangerouslySetInnerHTML` を通して表示しています。

```jsx
<div
  dangerouslySetInnerHTML={{
    __html: `${blog.body}`,
  }}
/>
```

APIから返される記事本文は文字列形式（HTMLタグも文字列として取得される）なので、これをHTMLとして描画するために`dangerouslySetInnerHTML` を使っています。


## 7. コードブロックのハイライトを行う

microCMSのリッチエディタでソースコードとして記述した部分は、

```jsx
<pre>
  <code>
    // コード
  </code>
</pre>
```

という形式で取得されます。

このまま`dangerouslySetInnerHTML`を通すだけでは、何もハイライトされていないコードが表示されるので、ライブラリを使って装飾していきます。

microCMSの公式の記事でも紹介されている、[cheerio](https://www.npmjs.com/package/cheerio)と[highlight.js](https://www.npmjs.com/package/highlight.js)というライブラリを使ってサーバーサイドでハイライトを行いました。

https://blog.microcms.io/syntax-highlighting-on-server-side/

まずはライブラリをインストールします。

```
$ yarn add highlight.js cheerio
$ yarn add --dev @types/highlightjs @types/cheerio
```

次にブログ記事を生成する際の `getStaticProps` 内の記述を修正します。
※ライブラリについての説明は上記のmicroCMSの記事などを参照ください。

```jsx
// pages/blog/[id].tsx

// importを追記
import cheerio from "cheerio";
import hljs from "highlight.js";
import "highlight.js/styles/hybrid.css";

// 中略

export const getStaticProps: GetStaticProps<Props, Params> = async (
  context
) => {
  const id = context.params?.id;
  const blog = await client.get({ endpoint: "blog", contentId: id });
  // 以下の部分を追記
  const $ = cheerio.load(blog.body);
  $("pre code").each((_, elm) => {
    const result = hljs.highlightAuto($(elm).text());
    $(elm).html(result.value);
    $(elm).addClass("hljs");
  });

  return {
    props: {
      blog,
      highlightedBody: $.html(),
    },
  };
};
```

これでサーバーサード側でシンタックスハイライト済の記事データを取得することができます。

※実装においては以下の記事も参考にさせていただきました。

https://www.blogchin.net/blogs/p9f3s1x5k5yb/

https://qiita.com/cawauchi/items/ff6489b17800c5676908

以上で`microCMS`と`Next.js`を組み合わせたブログ構築の基本の手順は完了です。

ここからは、このブログサイトに実装した機能を少しご紹介したいと思います。

## 8. 【おまけ】タグでの絞り込み機能の実装

先述の通り、今回は `blog` と `tag` の2つのAPIを作成し、ブログ記事は複数の `tag` を持てるようにしました。

そして

- 記事一覧画面で各記事が持つタグを表示
- APIから取得した `tag` の一覧をサイドバーに表示（タグを選んで記事を絞り込む）

を出来るようにしました。

![tag_serch.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/f080023f-de03-b8fc-3fd4-11395b7db36e.jpeg)


### メリット

 `tag` のAPIエンドポイントを別途作成する構成にしたことで、以下のようなメリットがあります。

- サイドバーに設置した `tag` 一覧の表示順の並べ替えや修正を、コードをいじらずmicroCMSの管理画面で行える。
- `tag` 名を修正したい時など、記事のタグを一つひとつ修正しなくともAPIの `tag` を修正すれば一括して変更できる。

### 実装方法（記事一覧の表示について）

まずは、 `getStaticProps` から受け取った記事をそのまま全て表示するのではなく、画面に表示する記事だけを格納する `state` を別途作成します。

```jsx
const [showBlogs, setShowBlogs] = useState(blogs)
// 初期値にgetStaticPropsから受け取ったブログ記事データを入れる
```

そして画面には `showBlogs` の中身を `map` で回して一覧表示します。
※絞り込みによって `showBlogs` に記事が入っていなかった場合のための条件付きレンダリングも記述しておきます。

```jsx
{!showBlogs.length && <p>There are no posts...</p>}
{showBlogs.map((blog) => (
    // 記事を表示
```
　

**※注**
**このブログサイトはMaterialUIを使ってスタイリングを行っており、ここからは実際に実装しあ通り、通常のHTMLタグではなく`MaterialUI`のコンポーネントのタグで表記させていただきます。**
**（MaterialUIのタグ名を見ればどんな役割の要素なのか想像がつくと思うので、MaterialUIに触れたことが無い方も何となく読み取っていただけるかなと思います。）**
　

記事の一覧表示の中で、この記事が持つ `tag` も `map` で回して表示します。

```jsx
{showBlogs.map((blog) => (

    // 中略

    // 持っているtagを全て表示
    {blog.tags.map((tag) => (
      <Typography key={tag.id}>
        #{tag.tag}
      </Typography>
    ))}
```

### 実装方法（タグによる絞り込みについて）

`tag` のAPIエンドポイントから受け取るデータは、以下の`tag`型の配列でした。

```jsx
export type Tag = {
  id: string;
  tag: string;
  createdAt: string;
  updatedAt: string;
  publishedAt: string;
  revisedAt: string;
};
```

なのでまずはこのデータから、tag名だけを抜き出して配列に格納します。

```jsx
// getStaticPropsで取得したtagsからtag名のみ抜き出す
const tagList = tags.map((tag) => tag.tag);
```

作成した `tagList` を使って、サイドバーにタグ一覧を表示します。

```jsx
<List>
  <Typography>
    # Tags
  </Typography>

  <ListItemButton onClick={() => selectTag("all")}>
    <ListItemText primary="All"/>   // primaryに指定した文字列が表示される
  </ListItemButton>

  {tagList.map((tag) => (
    <ListItemButton key={tag} onClick={() => selectTag(tag)}>
      <ListItemText primary={tag}/>   // primaryに指定した文字列が表示される
    </ListItemButton>
  ))}
</List>
```

 `onClick` に指定した `selectTag()` メソッドを実装します。

```jsx
// タグ絞り込み
const selectTag = (tag: string) => {
  if (tag === "all") {
    setShowBlogs(blogs);
  } else {
    const selectedBlogs = blogs.filter((blog) => {
      const haveTags = blog.tags.map((tag) => tag.tag);
      return haveTags.includes(tag);
    });
    setShowBlogs(selectedBlogs);
  }

  // 画面最上部へスクロールさせる
  window.scrollTo({
    top: 0,
    behavior: "smooth",
  });
};
```

検索のロジックについては以前別の記事に書いたので、詳しくはこちらをご覧ください。
[Reactでリアルタイムの検索機能を実装する - Qiita](https://qiita.com/hinako_n/items/a0745afe0631578c698e)

以上で「All」を選べば全記事が表示され、タグを選べばそのタグを持つ記事のみが表示されるようになりました。

## 9.【おまけ】ページネーション機能の実装
`react-paginate`というライブラリを使って、ページネーション機能を実装しました。

https://www.npmjs.com/package/react-paginate

![ページネーション.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1208065/59f08d68-97e9-705d-8614-f1135c9fe76f.jpeg)
ライブラリについての詳しい説明では割愛し、ここでは実装したコードだけ載せさせていただきます。
使い方は以下の記事を参考にさせていただきました。

https://de-milestones.com/react_pagination/

https://tech.stmn.co.jp/entry/2020/10/28/141406

### 実装手順
まずはライブラリをインストールします。

```
$ yarn add react-paginate
```

記事一覧画面を編集していきます。

ページネーションの制御に使う `state`と、ページネーションをクリックした時に実行するメソッドを定義します。

```jsx:pages/index.tsx
import ReactPaginate from "react-paginate";


// 中略

  const [offset, setOffset] = useState(0);    // 何番目の記事から表示するか
  const perPage = 6;    // 1ページあたりに表示する記事数

  const handlePageChange = (data: { selected: number }) => {
    // クリックしたページ数を{selected: 1}のようなオブジェクト形式で引数に受ける
    setOffset(data.selected * perPage)   // 表示する記事の開始位置を変更

    // ページ最上部へスクロールする
    window.scrollTo({    
      top: 0,
      behavior: "smooth",
    });
  };
```

記事の一覧表示の箇所を修正します。

```jsx:pages/index.tsx
// 表示する記事をsliceで抽出して一覧表示
{showBlogs.slice(offset, offset + perPage).map((blog) => (
```

ページネーションコンポーネントを記事一覧の下に設置します。

```jsx:pages/index.tsx
// ページネーションコンポーネント
<ReactPaginate
   previousLabel={"<"}  // 前のページボタン
   nextLabel={">"}    // 次のページボタン
   pageCount={Math.ceil(blogs.length / perPage)}   // ページ総数
   onPageChange={handlePageChange}   // クリック時のfunction
   containerClassName={"pagination"}   // ページネーションであるulに付くクラス名
   activeClassName={"active"}   // アクティブなページのliに着くクラス名
/>
```

スタイルを付けます。

```css
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
  padding: 40px 0;
}

.pagination li {
  margin: 0 16px;
}

.pagination li > a {
  position: relative;
  font-size: 16px;
  width: 24px;
  height: 24px;
  outline: none;
  z-index: 100;
  cursor: pointer;
}

.pagination a::before {
  content: "";
  display: block;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  z-index: -100;
}

.pagination li.active > a::before {
  background-color: #929191;
}

.pagination li.active > a {
  color: #f2f2f2;
}
```

以上でページネーションを実装できました。

予めフロント側で全件保持している記事データに対して表示を変えているだけなので、もちろんリロードもなく瞬時に切り替わります。


## 最後に
microCMSは公式のチュートリアルやブログが豊富で分かりやすく、機能もどんどん拡張されており（最初は公式SDKも無かったらしい）、長く使っていくにも安心だなと思いました。

また業務で`Next.js`と`TypeScript`はいつも使っていますが、自分で`ESLint`や`Prettier`の設定をしたことは無く理解せず使っていたので、自分で調べて設定をすると勉強になりました。

このブログは最低限の機能を作ってデプロイしたのでまだまだ未完成ですが、これから少しずつ機能拡大・スタイル修正をしていきたいなと思います。


## 参考記事

https://zenn.dev/elletech/articles/nextjs_microcms

https://blog.microcms.io/microcms-next-jamstack-blog/

https://fwywd.com/tech/next-eslint-prettier