chakra-uiのインストール
chakraの公式サイトにあるコマンドを叩く
% yarn add @chakra-ui/react @emotion/react @emotion/styled framer-motion
後々iconsも使うことになったので、これもインストールしときます
% yarn add @chakra-ui/icons
なんかコンパイルエラー出た
Could not find a declaration file for module ‘lodash’
というエラーが出た。
lodashの型が必要らしい
以下のコマンドでlodashの型をインストールします。
% yarn add @types/lodash
tsconfig.json
内のconpilerOptions.types
を以下のように変更。
{
"compilerOptions": {
...
- "incremental": true
+ "incremental": true,
+ "types" : ["node", "lodash"]
},
}
chakra使えてるか確認
chakraを使うには使いたい要素をChakraProvider
タグで囲む必要がある。
_app.tsx
を以下のように編集。
import type { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'
function MyApp({ Component, pageProps }: AppProps) {
return (
<ChakraProvider>
<Component {...pageProps} />
</ChakraProvider>
)
}
export default MyApp
これでchakra使えるはずなので、トップページを編集します。
import { Text } from '@chakra-ui/react'
import type { NextPage } from 'next'
const Home: NextPage = () => {
return (
<Text color='red'>hello world</Text>
)
}
export default Home
レイアウトコンポーネントで、レイアウトを共通化する
ヘッダーやフッターなどを各ページで共通化したいので、Next.jsのドキュメントに従ってやっていきます。
ヘッダーはサンプルを参考にして以下のように仮作成しました。(Footerはとりあえず空の要素を返すだけにしてます)
import React from "react";
import {
Box,
Stack,
Heading,
Flex,
Text,
Button,
useDisclosure
} from "@chakra-ui/react";
import { HamburgerIcon } from "@chakra-ui/icons";
export const Header = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const handleToggle = () => (isOpen ? onClose() : onOpen());
return (
<Flex
as="nav"
align="center"
justify="space-between"
wrap="wrap"
padding={6}
bg="teal.500"
color="white"
>
<Flex align="center" mr={5}>
<Heading as="h1" size="lg" letterSpacing={"tighter"}>
Chakra UI
</Heading>
</Flex>
<Box display={{ base: "block", md: "none" }} onClick={handleToggle}>
<HamburgerIcon />
</Box>
<Stack
direction={{ base: "column", md: "row" }}
display={{ base: isOpen ? "block" : "none", md: "flex" }}
width={{ base: "full", md: "auto" }}
alignItems="center"
flexGrow={1}
mt={{ base: 4, md: 0 }}
>
<Text>Docs</Text>
<Text>Examples</Text>
<Text>Blog</Text>
</Stack>
<Box
display={{ base: isOpen ? "block" : "none", md: "block" }}
mt={{ base: 4, md: 0 }}
>
<Button
variant="outline"
_hover={{ bg: "teal.700", borderColor: "teal.700" }}
>
Create account
</Button>
</Box>
</Flex>
);
};
共通化したいレイアウトは以下のようなものです。
import { Footer } from './Footer'
import { FC, ReactNode } from 'react'
import { Header } from './Header'
type Props = {
children: ReactNode
}
export const Layout: FC<Props> = ({ children }) => {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
)
}
このLayout
コンポーネントでメインとなる要素を囲めば、ヘッダーとフッターが表示されるという寸法です。
レイアウトの実装には2通りあり、全ページ共通のレイアウトを_app.tsx
で設定するやり方と、各ページのindex.tsx
からgetLayout
メソッドを呼んでレイアウトを使うか使わないか、もしくはどのレイアウトを使うのかを指定するやり方があるそうです。
ゆくゆくはadminページも作れればいいなと思っているので、今回はadmin用ページとユーザー用ページでレイアウトを出し分けられるように後者のやり方を参考にします。
app.tsx
を以下のように編集。
import type { ReactElement, ReactNode } from 'react'
import type { NextPage } from 'next'
import type { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const getLayout = Component.getLayout ?? ((page) => page)
return (
<ChakraProvider>
{getLayout(<Component {...pageProps} />)}
</ChakraProvider>
)
}
これで例えばトップページで先ほど作ったLayoutコンポーネントを使用したい場合は以下のようにします。
import { Box } from '@chakra-ui/react'
import type { ReactElement } from 'react'
import { Layout } from '../components/common/Layout'
import type { NextPageWithLayout } from './_app'
const Page: NextPageWithLayout = () => {
return <Box>hello world</Box>
}
Page.getLayout = function getLayout(page: ReactElement) {
return (
<Layout>
{page}
</Layout>
)
}
export default Page
レイアウトを使用せず、メインコンテンツをそのまま表示したい場合はreturn
内を
return page
のみにし、
別のレイアウトを使いたい場合は
return (
<AnotherLayout>
{page}
</AnotherLayout>
)
のようにすればよさそうです。
またこのNext.jsのレイアウト機能を使うと、ページを遷移してもレイアウト部分のstateを保持してくれるらしいです。
おkおk。