Pagination() — supabase Function Reference
Architecture documentation for the Pagination() function in Pagination.tsx from the supabase codebase.
Entity Profile
Dependency Diagram
graph TD 5a9eefc0_f917_352f_fd90_15900c850758["Pagination()"] b76cb636_83ff_c578_eedc_e0814bacbc7c["formatEstimatedCount()"] 5a9eefc0_f917_352f_fd90_15900c850758 -->|calls| b76cb636_83ff_c578_eedc_e0814bacbc7c style 5a9eefc0_f917_352f_fd90_15900c850758 fill:#6366f1,stroke:#818cf8,color:#fff
Relationship Graph
Source Code
apps/studio/components/grid/components/footer/pagination/Pagination.tsx lines 55–397
export const Pagination = ({ enableForeignRowsQuery = true }: PaginationProps) => {
const { id: _id } = useParams()
const id = _id ? Number(_id) : undefined
const { sorts } = useTableSort()
const { filters } = useTableFilter()
const { data: project } = useSelectedProjectQuery()
const tableEditorSnap = useTableEditorStateSnapshot()
const snap = useTableEditorTableStateSnapshot()
const roleImpersonationState = useRoleImpersonationStateSnapshot()
const { data: selectedTable } = useTableEditorQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
id,
})
const isForeignTableSelected = isForeignTable(selectedTable)
const page = snap.page
// rowsCountEstimate is only applicable to table entities
const rowsCountEstimate = isTable(selectedTable) ? selectedTable.live_rows_estimate : null
const [value, setValue] = useState<string>(page.toString())
const [isConfirmNextModalOpen, setIsConfirmNextModalOpen] = useState(false)
const [isConfirmPreviousModalOpen, setIsConfirmPreviousModalOpen] = useState(false)
const [isConfirmFetchExactCountModalOpen, setIsConfirmFetchExactCountModalOpen] = useState(false)
const {
data,
isPending: isLoading,
isSuccess,
isError,
isFetching,
error,
} = useTableRowsCountQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
tableId: snap.table.id,
filters,
enforceExactCount: snap.enforceExactCount,
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
},
{
placeholderData: keepPreviousData,
enabled: !isForeignTableSelected,
}
)
const count = data?.count ?? 0
const countString = data?.is_estimate ? formatEstimatedCount(count) : count.toLocaleString()
const maxPages = Math.ceil(count / tableEditorSnap.rowsPerPage)
const totalPages = count > 0 ? maxPages : 1
// [Joshen] This is only applicable for foreign tables, as we use the number of rows on the page to determine
// if we've reached the last page (and hence disable the next button)
const { data: rowsData, isPending: isLoadingRows } = useTableRowsQuery(
{
projectRef: project?.ref,
connectionString: project?.connectionString,
tableId: id,
sorts,
filters,
page: snap.page,
limit: tableEditorSnap.rowsPerPage,
roleImpersonationState: roleImpersonationState as RoleImpersonationState,
},
{
enabled: isForeignTableSelected && enableForeignRowsQuery,
}
)
const isLastPage = (rowsData?.rows ?? []).length < tableEditorSnap.rowsPerPage
const onPreviousPage = () => {
if (page > 1) {
if (snap.selectedRows.size >= 1) {
setIsConfirmPreviousModalOpen(true)
} else {
goToPreviousPage()
}
}
}
const onConfirmPreviousPage = () => {
goToPreviousPage()
setIsConfirmPreviousModalOpen(false)
}
const onNextPage = () => {
if (page < maxPages) {
if (snap.selectedRows.size >= 1) {
setIsConfirmNextModalOpen(true)
} else {
goToNextPage()
}
}
}
const onConfirmNextPage = () => {
goToNextPage()
setIsConfirmNextModalOpen(false)
}
const goToPreviousPage = () => {
const previousPage = page - 1
snap.setPage(previousPage)
}
const goToNextPage = () => {
const nextPage = page + 1
snap.setPage(nextPage)
}
const onPageChange = (page: number) => {
const pageNum = page > maxPages ? maxPages : page
snap.setPage(pageNum || 1)
}
const onRowsPerPageChange = (value: string | number) => {
const rowsPerPage = Number(value)
tableEditorSnap.setRowsPerPage(isNaN(rowsPerPage) ? 100 : rowsPerPage)
}
// keep input value in-sync with actual page
useEffect(() => {
setValue(String(page))
}, [page])
useEffect(() => {
if (!isForeignTableSelected && page && page > totalPages) {
snap.setPage(totalPages)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isForeignTableSelected, page, totalPages])
useEffect(() => {
if (id !== undefined) {
snap.setEnforceExactCount(rowsCountEstimate !== null && rowsCountEstimate <= THRESHOLD_COUNT)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id])
useEffect(() => {
// If the count query encountered a timeout error with exact count
// turn off the exact count to rely on approximate
if (isError && snap.enforceExactCount && error?.code === 408) {
snap.setEnforceExactCount(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isError, snap.enforceExactCount, error?.code])
if (isForeignTableSelected) {
return (
<div className="flex items-center gap-x-2">
<Button
aria-label="Previous page"
icon={<ArrowLeft />}
type="outline"
className="px-1.5"
disabled={page <= 1}
onClick={onPreviousPage}
/>
<p className="text-xs text-foreground-light">Page</p>
<Input
size="tiny"
className="w-10"
min={1}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => {
const parsedValue = Number(value)
if (
(e.code === 'Enter' || e.code === 'NumpadEnter') &&
!Number.isNaN(parsedValue) &&
parsedValue >= 1
) {
onPageChange(parsedValue)
}
}}
/>
<Button
aria-label="Next page"
icon={<ArrowRight />}
type="outline"
className="px-1.5"
disabled={isLastPage || !enableForeignRowsQuery}
loading={isLoadingRows && enableForeignRowsQuery}
onClick={goToNextPage}
/>
<RowCountSelector onRowsPerPageChange={onRowsPerPageChange} />
</div>
)
}
return (
<div className="flex items-center gap-x-4">
{isLoading && (
<div className="flex items-center gap-x-2">
<Loader2 size={12} className="animate-spin" />
<p className="text-xs text-foreground-light">Loading records count...</p>
</div>
)}
{isSuccess && (
<>
<div className="flex items-center gap-x-2">
<Button
aria-label="Previous page"
icon={<ArrowLeft />}
type="outline"
className="px-1.5"
disabled={page <= 1 || isLoading}
onClick={onPreviousPage}
/>
<p className="text-xs text-foreground-light">Page</p>
<Input
className="w-12"
size="tiny"
min={1}
max={maxPages}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={(e) => {
const parsedValue = Number(value)
if (
(e.code === 'Enter' || e.code === 'NumpadEnter') &&
!Number.isNaN(parsedValue) &&
parsedValue >= 1 &&
parsedValue <= maxPages
) {
onPageChange(parsedValue)
}
}}
/>
<p className="text-xs text-foreground-light">of {totalPages.toLocaleString()}</p>
<Button
aria-label="Next page"
icon={<ArrowRight />}
type="outline"
className="px-1.5"
disabled={page >= maxPages || isLoading}
onClick={onNextPage}
/>
<RowCountSelector onRowsPerPageChange={onRowsPerPageChange} />
</div>
{!isForeignTableSelected && (
<div className="flex items-center gap-x-2">
<p className="text-xs text-foreground-light">
{`${countString} ${count === 0 || count > 1 ? `records` : 'record'}`}{' '}
{data.is_estimate ? '(estimated)' : ''}
</p>
{data.is_estimate && (
<Tooltip>
<TooltipTrigger asChild>
<Button
size="tiny"
type="text"
className="px-1.5"
loading={isFetching}
icon={<HelpCircle />}
onClick={() => {
// Show warning if either NOT a table entity, or table rows estimate is beyond threshold
if (rowsCountEstimate === null || count > THRESHOLD_COUNT) {
setIsConfirmFetchExactCountModalOpen(true)
} else snap.setEnforceExactCount(true)
}}
/>
</TooltipTrigger>
<TooltipContent side="top" className="w-72">
This is an estimated value as your table has more than{' '}
{THRESHOLD_COUNT.toLocaleString()} rows. <br />
<span className="text-brand">
Click to retrieve the exact count of the table.
</span>
</TooltipContent>
</Tooltip>
)}
</div>
)}
</>
)}
{isError && (
<p className="text-sm text-foreground-light">
Error fetching records count. Please refresh the page.
</p>
)}
<ConfirmationModal
visible={isConfirmPreviousModalOpen}
title="Confirm moving to previous page"
confirmLabel="Confirm"
onCancel={() => setIsConfirmPreviousModalOpen(false)}
onConfirm={() => {
onConfirmPreviousPage()
}}
>
<p className="text-sm text-foreground-light">
The currently selected lines will be deselected, do you want to proceed?
</p>
</ConfirmationModal>
<ConfirmationModal
visible={isConfirmNextModalOpen}
title="Confirm moving to next page"
confirmLabel="Confirm"
onCancel={() => setIsConfirmNextModalOpen(false)}
onConfirm={() => {
onConfirmNextPage()
}}
>
<p className="text-sm text-foreground-light">
The currently selected lines will be deselected, do you want to proceed?
</p>
</ConfirmationModal>
<ConfirmationModal
variant="warning"
visible={isConfirmFetchExactCountModalOpen}
title="Confirm to fetch exact count for table"
confirmLabel="Retrieve exact count"
onCancel={() => setIsConfirmFetchExactCountModalOpen(false)}
onConfirm={() => {
snap.setEnforceExactCount(true)
setIsConfirmFetchExactCountModalOpen(false)
}}
>
<p className="text-sm text-foreground-light">
{rowsCountEstimate === null
? `If your table has a row count of greater than ${THRESHOLD_COUNT.toLocaleString()} rows,
retrieving the exact count of the table may cause performance issues on your database.`
: `Your table has a row count of greater than ${THRESHOLD_COUNT.toLocaleString()} rows, and
retrieving the exact count of the table may cause performance issues on your database.`}
</p>
</ConfirmationModal>
</div>
)
}
Domain
Subdomains
Calls
Source
Frequently Asked Questions
What does Pagination() do?
Pagination() is a function in the supabase codebase.
What does Pagination() call?
Pagination() calls 1 function(s): formatEstimatedCount.
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free