api response
{
"data": [
...
],
"links": {
"first": "https://example.com/pagination?page=1",
"last": "https://example.com/pagination?page=10",
"prev": null,
"next": "https://example.com/pagination?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 10,
"links": [
...
],
"path": "https://example.com/pagination",
"per_page": 15,
"to": 15,
"total": 150
}
}
main.tsx
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 1,
pageSize: 10
})
const {
data
} = useQuery({
queryKey: ['tasks', pagination],
queryFn: () => fetchTasks(pagination),
placeholderData: keepPreviousData
})
const table = useReactTable({
data: data?.rows ?? defaultData,
columns,
rowCount: data?.rowCount,
state: {
pagination,
columnPinning: {
right: ['action']
}
},
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
debugTable: true
})
<Table></Table>
<TablePagination data={data} pagination={pagination} table={table} />
fetch.ts
import fetchClient from '@/lib/fetch-client'
import { tableParser } from '@/lib/utils'
import QueryString from 'qs'
interface FetchTaskProps {
pageIndex: number
pageSize: number
}
export async function fetchTasks({ pageIndex, pageSize }: FetchTaskProps) {
const url = process.env.NEXT_PUBLIC_API_URL + '/api/tasks'
const query = QueryString.stringify({
include: 'farms,workers,settings',
page: pageIndex,
paginate: pageSize
})
const response = await fetchClient({
url: `${url}?${query}`,
method: 'GET'
})
const data = await response.json()
return tableParser({ data, pageSize })
}
utils.ts
export const tableParser = ({
data,
pageSize
}: {
data: any
pageSize: number
}) => {
const { data: rows, ...rest } = data
const elements = () => {
let current = rest.meta.current_page
let last = rest.meta.last_page
let delta = 1
let left = current - delta
let right = current + delta + 1
let range = []
let rangeWithDots = []
let l
// use rangeWithDotsMax to limit the number of elements in the pagination
for (let i = 1; i <= last; i++) {
if (i == 1 || i == last || (i >= left && i < right)) {
range.push(i)
}
}
for (let i of range) {
if (l) {
if (i - l === 2) {
rangeWithDots.push(l + 1)
} else if (i - l !== 1) {
rangeWithDots.push('...')
}
}
rangeWithDots.push(i)
l = i
}
console.log({
range,
rangeWithDots
})
return rangeWithDots
}
return {
rows,
elements: elements(),
pageCount: Math.ceil(rows.length / pageSize),
rowCount: rest.meta.total,
...rest
}
}
table-pagination.tsx
import { ChevronLeftIcon, ChevronRightIcon } from '@radix-ui/react-icons'
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious
} from '@/components/ui/pagination'
import { cn } from '@/lib/utils'
import type { PaginationState } from '@tanstack/react-table'
const TablePagination = ({
data,
pagination,
table
}: {
data: any
pagination: PaginationState
table: any
}) => {
const prevPage =
pagination.pageIndex === 2
? 0
: pagination.pageIndex - 1 < 0
? 0
: pagination.pageIndex - 1
const nextPage =
pagination.pageIndex === 0
? pagination.pageIndex + 2
: pagination.pageIndex + 1
return (
<Pagination className="items-end justify-end py-4">
<PaginationContent>
<PaginationItem>
<PaginationPrevious
onClick={() => {
if (prevPage === 2) return
table.setPageIndex(prevPage)
}}
className={cn('size-9 p-0 cursor-pointer', {
'cursor-not-allowed': nextPage === 2
})}
>
<ChevronLeftIcon className="size-4" />
</PaginationPrevious>
</PaginationItem>
{data?.elements.map((element: any, index: number) => {
if (element === '...') {
return (
<PaginationEllipsis key={index}>
<span>...</span>
</PaginationEllipsis>
)
}
return (
<PaginationItem key={index}>
<PaginationLink
className="size-9 p-0 cursor-pointer"
isActive={
element ===
(pagination.pageIndex === 0
? pagination.pageIndex + 1
: pagination.pageIndex)
}
onClick={() => {
table.setPageIndex(element)
}}
>
{element}
</PaginationLink>
</PaginationItem>
)
})}
<PaginationItem>
<PaginationNext
onClick={() => {
if (nextPage > data?.meta.last_page) return
table.setPageIndex(nextPage)
}}
className={cn('size-9 p-0 cursor-pointer', {
'cursor-not-allowed': nextPage > data?.meta.last_page
})}
>
<ChevronRightIcon className="size-4" />
</PaginationNext>
</PaginationItem>
</PaginationContent>
</Pagination>
)
}
export default TablePagination