Qlitre's Blog

2022.07.17 Next.js /microCMS

Next.js, microCMS, Chakra UIで作るブログ ②トップページの作成

Next.js, microCMS, Chakra-UIで作るブログシリーズの2つ目の記事です。
今回はトップページの作成を行っていきます。

ヘッダーの作成

まずはヘッダーの作成をしていきます。

srcディレクトリにcomponentsディレクトリを作り、パーツを記述していくようにしましょう。
Headet.tsxを作成し以下のようにします。

/* src/components/Header.tsx */
import {
    Box,
    Flex,
    Container,
    Heading,
} from '@chakra-ui/react';
import NextLink from "next/link";
import { FC } from 'react';

export const Header: FC = () => {    
    return (
        <Box px={4} bgColor="gray.100">
            <Container maxW="container.lg">
                <Flex as="header" py="4" justifyContent="space-between" alignItems="center">
                    <NextLink href="/" passHref>
                        <Heading as='h1' fontSize="2xl" cursor="pointer">
                            Next.js microCMS Blog
                        </Heading>
                    </NextLink>
                </Flex>
            </Container>
        </Box>
    );
}


次にindex.tsxを以下のようにして、読み込みます。

/* src/pages/index.tsx */
import type { NextPage } from 'next'
import { Header } from 'components/Header'

const Home: NextPage = () => {
  return (
    <Header />
  )
}

export default Home


yarn devして結果を確認しましょう。


このようにヘッダーが表示されます。

ダークモードの対応

Chakra UIを使うとダークモードの対応が簡単にできます。
ヘッダーに切り替えボタンを設置させていきます。
Header.tsxを以下のように編集します。

/* src/components/Header.tsx */
import {
    Box,
    Flex,
    Container,
    Heading,
    /* 追加 */
    useColorMode,
    useColorModeValue,
    Button
} from '@chakra-ui/react';
import NextLink from "next/link";
import { FC } from 'react';
// 追加
import { MoonIcon, SunIcon } from '@chakra-ui/icons';

export const Header: FC = () => {
    // 追加 カラーモードを切り替える
    const { colorMode, toggleColorMode } = useColorMode();
    return (
        /* 変更 ライトモードでgray.100,ダークモードでgray.900とする。 */
        <Box bg={useColorModeValue('gray.100', 'gray.900')} px={4}>
            <Container maxW="container.lg">
                <Flex as="header" py="4" justifyContent="space-between" alignItems="center">
                    <NextLink href="/" passHref>
                        {/* 変更 ライトモードでgray.600、ダークモードでwhiteとする */}
                        <Heading as='h1' fontSize="2xl" cursor="pointer" color={useColorModeValue('gray.600', 'white')}>
                            Next.js microCMS Blog
                        </Heading>
                    </NextLink>
                    {/* 追加 切り替えアイコン */}
                    <Button size='lg' onClick={toggleColorMode}>
                        {colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
                    </Button>
                </Flex>
            </Container>
        </Box>
    );
}


以下のようにライトモードの際はムーンのアイコンが出ます。



アイコンをクリックしますと…



ダークモードに切り替わりました。
一部のタグでライトモードとダークモードのカラーをそれぞれ設定しました。
が、基本的にはChakra UIのコンポーネントを使っていると、よしなに切り替えてくれるみたいです。
この辺の色合いは好みがあるので、公式を参考に色々といじってみると面白いかと思います。
https://chakra-ui.com/docs/styled-system/theme#colors

client.tsの作成

次に記事一覧を表示させていきたいですが、前準備としてmicroCMSにリクエストを投げるクライアントを作成しておきましょう。
srcディレクトリにlibsディレクトリを作り、client.tsを作成し、以下のようにします。

/* src/libs/client.ts */
import { createClient } from 'microcms-js-sdk'; //ES6

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


前回の記事で作成した.envファイルの情報を読み込み、こちらを使ってリクエストを投げる形です。

typeの記述

次に、今回のAPIのtypeを記述しておきましょう。
typesディレクトリにblog.tsを作成します。

基本的にmicroCMSの設定したAPIスキーマに沿って記述していきます。

/* src/types/blog.ts */
export type PostTag = {
    name: string
}

export type Post = {
    title: string;
    description: string,
    tag: PostTag[];
    text: string,
}


自分で設定したのはこれだけですが、その他、microCMSのAPIにはデフォルトで付与されるフィールドがあります。
idとかcreatedAtとかですね。
直接書き加えてもいいのですが、microcms-js-sdkでtypeを用意してくれています。

githubのtype.tsを見てみましょう。

     /**
	 * microCMS contentId
	 * https://document.microcms.io/manual/content-id-setting
	 */
	export interface MicroCMSContentId {
	  id: string;
	}
	
	/**
	 * microCMS content common date
	 */
	export interface MicroCMSDate {
	  createdAt: string;
	  updatedAt: string;
	  publishedAt?: string;
	  revisedAt?: string;
	}

	// ...省略    

	/**
	 * microCMS list content common types
	 */
	export type MicroCMSListContent = MicroCMSContentId & MicroCMSDate;


https://github.com/microcmsio/microcms-js-sdk/blob/main/src/types.ts

以上のようにtypeが記述されています。
なので、こちらをimportして結合するようにします。

/* src/types/blog.ts */
import type { MicroCMSListContent } from "microcms-js-sdk";

export type PostTag = {
    name: string
} & MicroCMSListContent

export type Post = {
    title: string;
    description: string,
    tag: PostTag[];
    text: string,
} & MicroCMSListContent


※PostTagはTagとしたかったですが、Chakra UIのTagコンポーネントと名前衝突が起きるため、PostTagとしました。

記事一覧の表示

これで準備が整ったので記事一覧を表示させていきましょう。
まずはChakra UIを使わずに適当に表示させます。適当な記事を事前にmicroCMSから作っておくようにしましょう。

/* src/pages/index.tsx */
import type { NextPage } from 'next'
import { Header } from 'components/Header'
import { client } from 'libs/client';
import { Post } from 'types/blog'

export const getStaticProps = async () => {
  const data = await client.getList({ endpoint: "post" });
  return {
    props: {
      posts: data.contents,
    },
  };
};

type Props = {
  posts: Post[];
};

const Home: NextPage<Props> = ({ posts }) => {
  return (
    <>
      <Header />
      <ul>
        {posts.map(post => (
          <div key={post.id}>
            <p>{post.title}</p>
            <p>{post.publishedAt}</p>
            <p>{post.description}</p>
          </div>
        ))}
      </ul>
    </>
  )
}

export default Home


とりあえず表示させることができました。

見た目をおしゃれにする

次にChakra UIを使って見た目を整えていきましょう。
componentsディレクトリにPostList.tsxを作成します。

import type { Post } from 'types/blog'
import {
    Box,
    Heading,
    Stack,
    Link,
    Text,
    Button,
} from "@chakra-ui/react";

type Props = {
    posts: Post[]
}

export const PostList = ({ posts }: Props) => {
    return (
        <>
            {posts.map(post => (
                <Box key={post.id}>
                    <Link href={`/post/${post.id}`}>
                        <Heading
                            as="h2"
                            fontSize="3xl"
                            lineHeight={1.6}
                            marginTop="1"
                            flex={1}
                            cursor="pointer"
                        >
                            {post.title}
                        </Heading>
                    </Link>
                    <Text fontSize="xl" color="gray.500" mt="2">{post.publishedAt}</Text>
                    <Text mt="1" fontSize="xl" color="gray.500">{post.description}</Text>
                    <Link href={`/post/${post.id}`}>
                        <Button colorScheme='teal' variant='outline' size="sm" mt="8">
                            続きを読む
                        </Button>
                    </Link>
                    <Stack mt="10" mb="10" borderBottom="1px" borderColor="gray.300" />
                </Box>
            ))}
        </>
    )
}


記述量が増えましたが、やっていることは先ほどと同じで、Chakraスタイルを加えているという感じです。

次にindex.tsxでこちらを読み込みます。

/* src/pages/index.tsx */
import type { NextPage } from 'next'
import { Header } from 'components/Header'
import { client } from 'libs/client';
import { Post } from 'types/blog'
// 追加
import { PostList } from 'components/PostList';
// 追加
import {
  Container,
  Heading
} from "@chakra-ui/react";

export const getStaticProps = async () => {
  const data = await client.getList({ endpoint: "post" });
  return {
    props: {
      posts: data.contents,
    },
  };
};

type Props = {
  posts: Post[];
};

const Home: NextPage<Props> = ({ posts }) => {
  return (
    <>
      <Header />
      <Container as="main" maxW="container.lg" marginTop="4" marginBottom="16">
        <Heading as="h2" fontSize="2xl" fontWeight="bold" mb="8">
          Home
        </Heading>
        <PostList posts={posts} />
      </Container>
    </>
  )
}

export default Home


大分見栄えがよくなってきました。

日付表記を直す

次に日付表記がちょっと見づらいので、直していきましょう。
microCMSの日付はUTC(協定世界時)で返るようです。

日付データはISO 8601形式のUTC(協定世界時)にて返却しています。
ご利用の際にはフロントエンド側にて現地時間への変換が必要です。(日本時間はプラス9時間)


https://help.microcms.io/ja/knowledge/specification-of-utc-time

こちらの公式のページを参考に日付を日本時間に直して返すDateTime.tsxをcomponents内に作成します。

dayjsを先にインストールしておきましょう。

yarn add dayjs


import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';


import { Text, Box } from "@chakra-ui/react";
import { FC } from "react";

type Props = {
    datetime: string;
}

export const DateTime: FC<Props> = ({ datetime }) => {
    dayjs.extend(utc)
    dayjs.extend(timezone);
    const formatDate = dayjs.utc(datetime).tz('Asia/Tokyo').format('YYYY-MM-DD')
    return (
        <Box mt="4">
            <Text as="time" dateTime={formatDate} fontSize="xl" color="gray.500">
                {formatDate}
            </Text>
        </Box>
    );
};


後はPostList.tsxで読み込みます。

// 追加
import { DateTime } from 'components/DateTime'

export const PostList = ({ posts }: Props) => {
    return (
        <>
            {posts.map(post => (
                <Box key={post.id}>
                    ...省略
                    {/* ここが変更 */}
                    <DateTime datetime={post.publishedAt} />
                    <Text mt="1" fontSize="xl" color="gray.500">{post.description}</Text>
                    ...省略
                </Box>
            ))}
        </>
    )
}




日付表記が直りました。

次回は記事詳細ページを作成していきます。