📄
Next.js / MUI (Material UI) でページネーション作成
Next.js で SSR しているサイトのページネーションを MUI(旧 Material UI)で作成する。
MUI には Pagination というコンポーネントがあるが、そのまま使うとカスタマイズしづらいので usePagination という hook を使う。
MUI をインストールする
// npm
npm install @mui/material @emotion/react
// yarn
yarn add @mui/material @emotion/react
Pagination コンポーネントを作成する
usePagination のサンプルコードを参考に Pagination コンポーネントを作成する。
Pagination.tsx
import usePagination from '@mui/material/usePagination'
import { css } from '@emotion/react'
export const Pagination = () => {
const { items } = usePagination({
count: 10,
})
return (
<nav>
<ul css={list}>
{items.map(({ page, type, selected, ...item }, index) => {
let children = null
if (type === 'start-ellipsis' || type === 'end-ellipsis') {
children = '…'
} else if (type === 'page') {
children = (
<button
type="button"
style={{
fontWeight: selected ? 'bold' : undefined,
}}
{...item}
>
{page}
</button>
)
} else {
children = (
<button type="button" {...item}>
{type}
</button>
)
}
return <li key={index}>{children}</li>
})}
</ul>
</nav>
)
}
const list = css`
list-style: none;
padding: 0;
margin: 0;
display: flex;
`
propsを追加する
Pagination コンポーネントの props に pageCount(ページ総数)と currentPage(現在のページ番号)を追加する
Pagination.tsx
+ import { FC } from 'react'
import usePagination from '@mui/material/usePagination'
import { css } from '@emotion/react'
+ type PaginationProps = {
+ pageCount: number
+ currentPage: number
+ }
- export const Pagination = () => {
+ export const Pagination: FC<PaginationProps> = ({ pageCount, currentPage }) => {
const { items } = usePagination({
- count: 10,
+ count: pageCount,
+ page: currentPage,
})
return (
<nav>
<ul css={list}>
{items.map(({ page, type, selected, ...item }, index) => {
let children = null
if (type === 'start-ellipsis' || type === 'end-ellipsis') {
children = '…'
} else if (type === 'page') {
children = (
<button
type="button"
style={{
fontWeight: selected ? 'bold' : undefined,
}}
{...item}
>
{page}
</button>
)
} else {
children = (
<button type="button" {...item}>
{type}
</button>
)
}
return <li key={index}>{children}</li>
})}
</ul>
</nav>
)
}
const list = css`
list-style: none;
padding: 0;
margin: 0;
display: flex;
`
next/link でページ遷移できるようにする
Pagination.tsx
import { FC } from 'react'
+ import Link from 'next/link'
+ import { useRouter } from 'next/router'
import usePagination from '@mui/material/usePagination'
import { css } from '@emotion/react'
type PaginationProps = {
pageCount: number
currentPage: number
}
export const Pagination: FC<PaginationProps> = ({pageCount, currentPage}) => {
+ const router = useRouter()
const { items } = usePagination({
count: pageCount,
page: currentPage,
})
return (
<nav>
<ul css={list}>
{items.map(({ page, type, selected, ...item }, index) => {
let children = null
if (type === 'start-ellipsis' || type === 'end-ellipsis') {
children = '…'
} else if (type === 'page') {
+ const query = { ...router.query, ...{ page } }
+ if (page === 1) {
+ delete query.page
+ }
children = (
- <button
- type="button"
- style={{
- fontWeight: selected ? 'bold' : undefined,
- }}
- {...item}
- >
- {page}
- </button>
+ <Link
+ href={{
+ pathname: router.pathname,
+ query
+ }}
+ passHref
+ >
+ <a>
+ <button
+ type="button"
+ style={{
+ fontWeight: selected ? 'bold' : undefined,
+ }}
+ {...item}
+ >
+ {page}
+ </button>
+ </a>
+ </Link>
)
- } else {
- children = (
- <button type="button" {...item}>
- {type}
- </button>
+ } else if (type === 'previous') {
+ const query = { ...router.query, ...{ page: currentPage - 1 } }
+ if (page === 1) {
+ delete query.page
+ }
+ children = (
+ <Link
+ href={{
+ pathname: router.pathname,
+ query
+ }}
+ passHref
+ >
+ <a>
+ <button type="button" {...item}>
+ {type}
+ </button>
+ </a>
+ </Link>
+ )
+ } else if (type === 'next') {
+ const query = { ...router.query, ...{ page: currentPage + 1 }
+ children = (
+ <Link
+ href={{
+ pathname: router.pathname,
+ query
+ }}
+ passHref
+ >
+ <a>
+ <button type="button" {...item}>
+ {type}
+ </button>
+ </a>
+ </Link>
)
}
return <li key={index}>{children}</li>
})}
</ul>
</nav>
)
}
const list = css`
list-style: none;
padding: 0;
margin: 0;
display: flex;
`
あとはお好みのスタイルをあてるだけ。
Discussion
記事の内容を生かしつつデモを作ってみました。
簡単ですが、以上です。