初心者がNext.js×TypeScript×microCMSでブログ作成
JavaScriptもNext.jsもTypeScriptもmicroCMSも初心者の私が、これらを組み合わせてブログを作成してみました。
ブログのリンク : https://tkblog.netlify.app/
ベースはmicroCMS公式ページ(https://blog.microcms.io/microcms-next-jamstack-blog/)に載っています。
しかし公式の例はTypeScriptではないので、いくつか変更を加えました。
他に追加した機能もあり記事内のコード例に含まれているのですが、それらについては別記事で紹介しようと思っています。
Next.jsのインストールまでは別記事で書いています。
takaittakait.hatenablog.com
目次
個別記事オブジェクトの型を定義
記事オブジェクトの型を定義するために、新たにファイルを作成しました。
root/types/article.ts
export type Article = { id: string createdAt: string updatedAt: string publishedAt: string revisedAt: string description: string title: string body: string eye_catch: { url: string } tag: string }
各フィールドの名前と型は、microCMSのAPIと合わせる必要があります。
記事一覧ページの中身を変更
root/papes/index.tsx
import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import React from 'react' import { client } from '../libs/client' import type { Article } from '../types/article' import styles from '../styles/Home.module.css' type props = { articles: Article[] }; export default function Home({ articles }: props) { return ( <div className={styles.container}> <Head> <title>記事一覧</title> <meta name="description" content="Next.jsでブログ作成" /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.indexmain}> <h1 className={styles.title}>記事一覧</h1> <div className={styles.grid}> {articles.map(article => ( <div key={article.id} className={styles.card}> <Link href={`/blog/${article.id}`}><a className=""> <Image className={styles.thumbnail} src={article.eye_catch.url || ''} width={600} height={400} alt={article.eye_catch.url}></Image> <p>{article.title}</p> </a></Link> </div> ))} </div> </main> </div> ); } export const getStaticProps = async () => { const data = await client.get({ endpoint: 'blog' }) return { props: { articles: data.contents, }, } }
ファイル名はindex.tsx
で固定です。これからこのファイル内の各処理について解説していきます。
型をインポート
import type { Article } from '../types/article'
先程作成したArticle型をインポートしています。
他のimport行は全て既存のもののインポートなので、書くだけで使えるはずです。
getStaticProps関数
export const getStaticProps = async () => { const data = await client.get({ endpoint: 'blog' }) return { props: { articles: data.contents, }, } }
順番が前後してしまいますが、getStaticProps
からご紹介します。
この関数を利用すると、ビルド時にデータを取得し、静的なHTMLを出力できます。リクエストが来る度にいちいちページ生成を行わないので、レスポンスが高速です。
endpoint
にはmicroCMSのAPIのエンドポイントを入れましょう。これでビルド時にmicroCMSのAPIにリクエストして、記事一覧を取得するようになります。
props
はリクエストして受けとったデータそのものです。フィールドのarticles
に、データに含まれる記事配列を入れています。このフィールド名articles
は後の手順で名前を合わせないといけないので注意が必要です。
Home関数
type props = { articles: Article[] }; export default function Home({ articles }: props) { return ( <div>sample</div> ); }
ここで新たな型を定義しています。props
型のフィールドarticles
は、getStaticProps
内で指定したフィールド名と合わせなければいけません。
Home
関数のexport
は恐らくJavaで言うpublicです。
Home{}の{}内で引数の設定をしています。
この例ではprops
オブジェクトを引数で受け取り、そのフィールドarticles
を引数内で利用する、という設定です。
関数だと、sampleFunc (name :string):string
というような書き方がより一般的なのかなと思います。
(引数名:引数の型):戻り値の型
という形式です。
default
が付いた関数はJSX.Element
型(≒HTML要素?)を返します。なのでreturn();
内に記事一覧ページのHTML要素を記述します。
個別記事ページを作成
root/pages/blog/[id].tsx
import { client } from "../../libs/client"; import styles from '../../styles/Home.module.css' import Head from 'next/head' import type { Article } from '../../types/article'; type props = { article: Article, }; export default function BlogId({article}:props) { return ( <div className={styles.container}> <Head> <title>{article.title}</title> <meta name="description" content={article.description} /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.postmain}> <h1 className={styles.title}>{article.title}</h1> <div className={styles.post} dangerouslySetInnerHTML={{__html: article.body}}/> </main> </div> ); } // パス指定 export const getStaticPaths = async () => { const data = await client.get({ endpoint: "blog" }); const articles: Article[] = data.contents; const paths = articles.map((content) => `/blog/${content.id}`); return { paths, fallback: false }; }; // データをテンプレートに受け渡す export const getStaticProps = async (context: any) => { const id = context.params.id; const res:Article = await client.get({ endpoint: "blog", contentId: id }); return { props: { article:res }, } };
また順番が前後しますが、getStaticPaths
→getStaticProps
→BlogId
の順で見ていきます。
getStaticPaths
// パス指定 export const getStaticPaths = async () => { const data = await client.get({ endpoint: "blog" }); const articles: Article[] = data.contents; const paths = articles.map((content) => `/blog/${content.id}`); return { paths, fallback: false }; };
名前通り、固定記事パスを指定しています。blog
はmicroCMSのAPIのエンドポイント、content.id
が各記事の識別子です。
getStaticProps
// データをテンプレートに受け渡す export const getStaticProps = async (context: any) => { const id = context.params.id; const res:Article = await client.get({ endpoint: "blog", contentId: id }); return { props: { article:res }, } };
BlogId(defaultの関数)
export default function BlogId({article}:props) { return ( <div className={styles.container}> <Head> <title>{article.title}</title> <meta name="description" content={article.description} /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.postmain}> <h1 className={styles.title}>{article.title}</h1> <div className={styles.post} dangerouslySetInnerHTML={{__html: article.body}}/> </main> </div> ); }
index.tsxのdefault
関数とほぼ同じ構造です。
<div className={styles.post} dangerouslySetInnerHTML={{__html: article.body}}/>
で、個別記事のHTML要素をテンプレート内に入れています。