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
次に記事一覧を表示させていきたいですが、前準備として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
ファイルの情報を読み込み、こちらを使ってリクエストを投げる形です。
次に、今回の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>
))}
</>
)
}
日付表記が直りました。
次回は記事詳細ページを作成していきます。