Next.js + microCMSでJamstackブログを作ろう(初心者編)
概要
今回は microCMS(HeadlessCMS) を使って、
Next.js + TypeScriptプロジェクトでJamstackブログをVercelにデプロイするまでをご紹介します。
事前準備
microCMS公式サイトで新規登録を済ませる。
※未登録の場合のみ。
microCMSとは
microCMSはAPIベースの日本製HeadlessCMSです。
主にブログのコンテンツ管理を担います。
環境
- Windows 10
- Vscode
- Node.js 18.13.0
- npm 8.19.3
- Next.js 13.1.6
- TypeScript 4.9.5
- microcms-js-sdk 2.3.2
- dayjs 1.11.7
1. Next.jsプロジェクトの準備
下記コマンドでNext.jsプロジェクトを作成します。
npx create-next-app@latest --typescript
詳しくは以下ページを参照ください。
2. microCMSを準備する
microCMS公式にログインする。
2-1. サービス作成画面へ
次にサービス作成画面に進みます
既にサービス名とサービスIDがランダムで振り分けられていますのでそのまま使います。
それではサービス作成ボタンを押して下さい。
※以下画像ではセキュリティ上消して載せています。
2-2. APIの作成
次に画面の中央に複数項目が表示されていますので、迷わずブログをクリックします。
すると自動的にブログを作成に必要なAPIが作成されますので。
今回はそのまま利用します。
※リスト形式になります。
microCMS側の準備は一旦これで完了です。
3. microcms-js-sdkの準備
それではプロジェクト側でmicroCMSのブログを表示の準備をしていきましょう。
以下コマンドをターミナルに入力しmicroCMSを使う準備をします。
npm i --save microcms-js-sdk
3-1. 投稿日時を標準時に整える
後で記事の投稿日時を標準時で表示するために事前にdayjsをインストールして置きます。
npm i dayjs
おまけ
見た目とかを整えたい方はbootstrapとbootstrap-iconsも入れておきます。
npm i bootstrap @types/bootstrap bootstrap-icons
4. Envファイルを作成しよう
APIキーをフロントに公開しないようにenvファイルで保護します。
プロジェクトフォルダに以下の名前のファイルを新規作成します。
.env.development.local
.envファイルが作成出来たら以下の様に記述し保存します。
API_KEY=
SERVICE_DOMAIN=
4-1. APIキーとサービスドメインを.envファイルに追加
次にmicroCMSのコンテンツ一覧ページで右上の少し下のAPIプレビューをクリック!
スライドが開きますのでX-MICROCMS-API-KEY:の右側XXXXXXXXXの部分をコピーして
先ほど作成した.envファイルのAPI_KEY=の右側に貼り付けます。
X-MICROCMS-API-KEY:XXXXXXXXX
サービスドメインはサービス作成時のサービスIDと同様なのですが、こちらもコンテンツ一覧画面の左上のサービス名の下に記載があるのでコピーします。
ではサービスドメインをenvファイルのSERVICE_DOMAIN=の右側に張り付けてください。
5. client.tsを作成する
src/
directory 内に libs/
directory を作成します。
次に libs/
directory 内に以下の名前のファイルを作成します。
client.ts
ファイルが作成出来たら以下の様にコードを追加し保存します。
import { createClient } from "microcms-js-sdk";
export const client = createClient({
serviceDomain: process.env.SERVICE_DOMAIN || "",
apiKey: process.env.API_KEY || "",
});
6. ブログ表示の準備
それではブログページを表示するまでの流れをご説明していきます。
6-1. 型を作っておく
src/
に types/
directory を作成する。
types/
の中に blog.ts
を作成する。
export type Blog = {
id: string;
createdAt: string;
updatedAt: string;
publishedAt: string;
revisedAt: string;
title: string;
body: string;
eyecatch: {
url: string;
width: number;
height: number;
};
category: [
{
name: string;
},
{
name: string;
},
{
name: string;
},
{
name: string;
},
{
name: string;
}
];
};
6-2. posts.tsxを作成する
src/
の中の pages/
内に posts.tsx
というファイルを作成する。
以下のコードを追加する。
import Link from "next/link";
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";
type Props = {
blog: Blog[];
};
const Posts: React.FC<Props> = ({ blog }) => {
return (
<div>
<ul>
{blog.map((blog) => (
<li key={blog.id}>
<Link href={`/blog/${blog.id}`}>{blog.title}</Link>
</li>
))}
</ul>
</div>
);
};
export default Posts;
export const getStaticProps = async () => {
const data = await client.get({ endpoint: "blog" });
return {
props: {
blog: data.contents,
},
};
};
6-3. [id].tsxを作成する
それでは src/
の中にある pages/
内に blog/
directory を作成しましょう。
作成した blog/
directory の中に以下の名前のファイルを作成する。
[ID].tsx
以下のコードを追加する。
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";
type Props = {
blog: Blog[];
};
const BlogId: React.FC<Props> = ({ blog }: any) => {
return (
<main>
<h1>{blog.title}</h1>
<p>{blog.publishedAt}</p>
<div
dangerouslySetInnerHTML={{
__html: `${blog.content}`,
}}
/>
</main>
);
};
export default BlogId;
export const getStaticPaths = async () => {
const data = await client.get({ endpoint: "blog" });
const paths = data.contents.map(
(content: { id: string }) => `/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 });
return {
props: {
blog: data,
},
};
};
6-4. ブログサイトを表示してみる
では、localhost:3000での表示が可能になりましたので試しに表示しましょう。
ターミナルで以下コマンドを入力します。
npm run dev
7. dayjsで投稿時間を正規化する
お気づきの方もいらっしゃられると思いますので、このままだと投稿日時が判りづらいので
dayjsで正規化します。
正規化するには以下のコードを追加します。
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
完成形のコードが以下になります。
import Link from "next/link";
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
import styles from "@/styles/Post.module.css";
import Image from "next/image";
type Props = {
blog: Blog[];
};
const Posts: React.FC<Props> = ({ blog }) => {
return (
<div>
<div className={styles.posts}>
<ul className={styles.grid}>
{blog.map((blog) => (
<li key={blog.id}>
<Link href={`/blog/${blog.id}`}>
<Image
className={styles.eyecatch_img}
src={blog.eyecatch.url}
width={300}
height={175}
alt={blog.title}
/>
<h5 className={styles.blogtitle}>{blog.title}</h5>
<p className={styles.publishedAt}>
投稿日:
{dayjs
.utc(blog.publishedAt)
.tz("Asia/Tokyo")
.format(
"YYYY" +
"年" +
"MM" +
"月" +
"DD" +
"日" +
"hh" +
":" +
"mm"
)}
</p>
</Link>
</li>
))}
</ul>
</div>
</div>
);
};
export default Posts;
// データをテンプレートに受け渡す部分の処理を記述します
export const getStaticProps = async () => {
const data = await client.get({ endpoint: "blog" });
return {
props: {
blog: data.contents,
},
};
};
import { client } from "@/libs/client";
import { Blog } from "@/types/blog";
import dayjs from "dayjs";
import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
dayjs.extend(utc);
dayjs.extend(timezone);
import styles from "@/styles/Blogid.module.css";
type Props = {
blog: Blog[];
};
const BlogId: React.FC<Props> = ({ blog }: any) => {
return (
<main>
<div>
<h1 className={styles.title}>{blog.title}</h1>
<p className={styles.publishedAt}>
投稿日:
{dayjs
.utc(blog.publishedAt)
.tz("Asia/Tokyo")
.format(
"YYYY" + "年" + "MM" + "月" + "DD" + "日" + "hh" + ":" + "mm"
)}
</p>
<p className={styles.category}>
タグ:{blog.category && `${blog.category.name}`}
</p>
<div
className={styles.post}
dangerouslySetInnerHTML={{ __html: blog.content }}
/>
</div>{" "}
</main>
);
};
export default BlogId;
export const getStaticPaths = async () => {
const data = await client.get({ endpoint: "blog" });
const paths = data.contents.map(
(content: { id: string }) => `/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 });
return {
props: {
blog: data,
},
};
};
8. Vercelにデプロイする
今回は詳しい説明は省きます。
詳しくは以下ページを参照ください。
8-1. Vercelに環境変数を設定する
Vercelの該当プロジェクトページを開き、画面の上にあるSettingタブをクリック。
次に画面左側のEnvironment Variablesをクリック。
Key と Value の入力する場所がありますのでそれぞれ以下の通り追加してください。
Key | Value |
---|---|
API_KEY | X-MICROCMS-API-KEY |
SERVICE_DOMAIN | サービスドメイン名 |
次にKey Valueの入力下にEnvironmentの項目があります。
Production と Development をクリックしチェックを付けます。
後はSaveを押して保存します。
これで本番環境でもブログページを表示する事が出来るようになります。
9. 完成したブログサイトを見てみよう
こちらがこの記事で作成したブログサイトです。
今回はデザイン等は一切考慮せずに作ったので興味のある方は次回も見て頂けると幸いです。
最後に
以上、Next.js +TypeScript プロジェクトでmicroCMSを使ったブログサイトを作ろうの記事でした。
ちなみにanyを使わずにコーディングする方法があれば良かったのですが、
筆者はTypescriptあんまり触ったことが無いので、変な書き方してたらすいません。
現段階では良い方法が解らなかったので、どなたかご教授下さる方がいれば幸いです。
追記
次回は以下2記事を追加予定です。
- Next.js + TypeScript SEO編
- Next.js + TypeScript + Bootstrap編
Discussion