Next.js 13の特徴をmicroCMSを使って構築しながら理解してみる
Next.js 13備忘録の背景と目的
Next.jsの情報をしばらく追ってなかった内に、 13がリリースされており、浦島太郎になっていたが、ある案件で使ってみたらめちゃ良かったので、その共有と不完全な理解を補足するために備忘録として書いていきます。
今回はまず、気になった主要機能と理解と、microCMSを使って、jamstack構成で雛形を作って見ることを目標にします。
下記について、今後執筆予定ですので、気になる方は、いいね!押してもらえると、頑張って早めに書いていきますので、どうぞよろしくお願いします🙇♂️
- Next13×tailwindでの@next/font/globalと@next/font/localを使ってみた
- Server Component使った実装とは 〜Static RenderingとDynamic Rendering〜
- Next.js 13×microCMSを使った検索機能を実装する際に迷ったServer ComponentとClient Componentの使い分け
- Next.js 13 App Router を使った際の多言語サイトの作り方
- Next.js 13でハマったPDF読み込みの実装方法
- Next.js 13×microCMSのみで実現する、APIキーを隠蔽しつつ、いいね機能の実装してみる
環境について
Node.js v19.4.0
Next.js 13.4.9
ソースはこちら
何はともあれNext.js 13のセットアップ
npx create-next-app@latest
設定はこんな感じ↓
✔ What is your project named? … next-microcms-sample
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias? … No
インストール、セットアップができたところで一度起動して確認してみてください
npm run dev
ついでに、microCMSように下記をインストールしましょう。
npm install microcms-js-sdk
microCMSの準備
microCMSでAPIを用意
APIキーとサービスドメインを取得します
取得したAPIキーとAPIのサービスドメイン名は環境変数ファイル.env.localに記載します。
MICROCMS_SERVICE_DOMAIN=サービスドメイン名
MICROCMS_API_KEY=APIキー
ページの作成
ここで、ページを作成する前に、Server ComponentsとClient Componentsの2種類がある。
さらには、Server Components中に大きく分けて2つの機能がある
- Static Rendering(従来のgetStaticProps≒SSGみたいなもの)
- Dynamic Rendering(従来のgetServerSideProps≒SSRや、getStaticPropsでのrevalidate利用した≒ISR)
名前からなんとなく想像しやすく、使い方も簡単だが、沼にハマりやすいので、解説はまた別記事にする。他の記事でも言及してることが多いので、何本か読むと理解できる気がする。
今回はデフォルトであるServer ComponentsのStatic Renderingのみを使って記事一覧と詳細ページを作ってみる。
参考
コンテンツ取得
まずは、コンテンツを取得するためのクライアント作成します。
libs/microcms.tsファイルとして型定義、クライアント、ブログ一覧の取得、ブログ詳細の取得を用意しておきます。
import { createClient } from "microcms-js-sdk";
import type {
MicroCMSQueries,
MicroCMSImage,
MicroCMSDate,
} from "microcms-js-sdk";
//ブログの型定義
export type Blog = {
id: string;
title: string;
content: string;
eyecatch?: MicroCMSImage;
} & MicroCMSDate;
if (!process.env.MICROCMS_SERVICE_DOMAIN) {
throw new Error("MICROCMS_SERVICE_DOMAIN is required");
}
if (!process.env.MICROCMS_API_KEY) {
throw new Error("MICROCMS_API_KEY is required");
}
// API取得用のクライアントを作成
export const client = createClient({
serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN,
apiKey: process.env.MICROCMS_API_KEY,
});
// ブログ一覧を取得
export const getList = async (queries?: MicroCMSQueries) => {
const listData = await client.getList<Blog>({
endpoint: "blogs",
queries,
});
// データの取得が目視しやすいよう明示的に遅延効果を追加
await new Promise((resolve) => setTimeout(resolve, 3000));
return listData;
};
// ブログの詳細を取得
export const getDetail = async (
contentId: string,
queries?: MicroCMSQueries
) => {
const detailData = await client.getListDetail<Blog>({
endpoint: "blogs",
contentId,
queries,
});
// データの取得が目視しやすいよう明示的に遅延効果を追加
await new Promise((resolve) => setTimeout(resolve, 3000));
return detailData;
}
記事一覧ページ
まずはStaticレンダリングを想定した記事一覧ページを作成します。app/static/page.tsxファイルを新規に作成します。
import Link from "next/link";
import { getList } from "../../lib/microcms";
export default async function StaticPage() {
const { contents } = await getList();
// ページの生成された時間を取得
const time = new Date().toLocaleString();
if (!contents || contents.length === 0) {
return <h1>No contents</h1>;
}
return (
<div>
<h1>{time}</h1>
<ul>
{contents.map((post) => {
return (
<li key={post.id}>
<Link href={`/static/${post.id}`}>{post.title}</Link>
</li>
);
})}
</ul>
</div>
);
}
記事詳細ページ
HTMLをパースするためhtml-react-parserをインストール
npm install html-react-parser
新規にstatic/[postId]/page.tsxファイルを作成します。
import { notFound } from "next/navigation";
import parse from "html-react-parser";
import { getDetail, getList } from "../../../lib/microcms";
export async function generateStaticParams() {
const { contents } = await getList();
const paths = contents.map((post) => {
return {
postId: post.id,
};
});
return [...paths];
}
export default async function StaticDetailPage({
params: { postId },
}: {
params: { postId: string };
}) {
const post = await getDetail(postId);
// ページの生成された時間を取得
const time = new Date().toLocaleString();
if (!post) {
notFound();
}
return (
<div>
<h1>{post.title}</h1>
<h2>{time}</h2>
<div>{parse(post.content)}</div>
</div>
);
}
動作の確認
npm run buildにて一度ビルドしてから動作を確認します。
まとめ・振り返り
今回作ってみて分かる通り、ディレクトリを見ると、従来のpageディレクトリではなく、appディレクトリとなっています。さらには、動作確認の際に、時間がビルドした時間になっているかと思います。これは静的サイトなっていることになっています。
また、fetchする際に、
export default async function StaticPage() {
const { contents } = await getList();
のように、Server Componentsでデータを取得するには、asyncとawaitを使用できます。
詳細は別記事にて説明する予定です。
次回は
この辺りについて、執筆予定です。
Discussion