@akhatmirzo/ui

Pagination

Split lists and tables into pages — real links for SEO, Next.js Link support and a compact mobile view.

21–30 / 230 ta

Import

import { Pagination, PaginationInfo } from '@akhatmirzo/ui';

When to use

  • Long lists and tables — orders, blog posts, search results. Users can jump straight to the page they need.
  • When the page belongs in the URL (/blog?page=3), pass getHref: the items become real <a href> links and search engines can discover every page.
  • Not for infinite feeds. For a social-style feed, a "Load more" button or automatic loading works better.
  • Skip it for short lists. If everything fits on one page, don't show pagination.

How pages are calculated

The page list comes from getPagination in @akhatmirzo/hooks — the same algorithm DataTable uses. The number of items stays stable (siblings * 2 + 5), so the buttons don't jump around when the page changes. The … is not clickable.

Pass total + pageSize (default 10) or pageCount directly. Pages start at 1.

Sizes

size — sm (32px) or md (40px, default) square buttons.

First / last page

showFirstLast adds ⏮ ⏭ buttons. siblings controls how many numbers appear next to the current page (default 1).

Compact view

compact="auto" (default) switches to ‹ 3 / 24 › on screens narrower than 640px. compact always shows the compact view, compact={false} always shows the full one.

Why CSS?

In auto mode both views are rendered and a CSS breakpoint hides one of them (display: none, so screen readers skip it too). There is no JavaScript matchMedia, so there is no SSR hydration mismatch and no layout jump on load.

With getHref, every page is a real <a href>. Clicking navigates as usual and also calls onPageChange. At the edges, "Previous" / "Next" render as disabled buttons, not links.

renderLink lets you plug in your own link component. Spread all props onto the link; page is the target page.

app/blog/page.tsx
import Link from 'next/link';
import { Pagination } from '@akhatmirzo/ui';

export default async function BlogPage({
  searchParams,
}: {
  searchParams: Promise<{ page?: string }>;
}) {
  const page = Number((await searchParams).page ?? 1);
  const { posts, total } = await getPosts(page);

  return (
    <>
      <PostList posts={posts} />
      <Pagination
        page={page}
        total={total}
        pageSize={10}
        getHref={(p) => `/blog?page=${p}`}
        renderLink={({ page, ...props }) => <Link {...props} />}
      />
    </>
  );
}

With DataTable

DataTable has its own pagination (the pagination prop). If you need to control paging outside the table — for example through the URL — leave pagination out and put Pagination + PaginationInfo below it.

№MijozSumma
3001Jasur Karimov120 000 so'm
3002Malika Rahimova434 159 so'm
3003Nodira Saidova748 318 so'm
3004Otabek Qodirov1 062 477 so'm
3005Jasur Karimov1 376 636 so'm

1–5 / 47 ta

Props

Pagination

Other <nav> attributes are passed through.

PropTypeDefaultDescription
pagenumber—Current page, 1-based (controlled)
defaultPagenumber1Initial page
onPageChange(page: number) => void—Called when the page changes
totalnumber—Total number of records
pageSizenumber10Records per page
pageCountnumber—Number of pages directly (instead of `total` / `pageSize`)
siblingsnumber1Numbers shown on each side of the current page
getHref(page: number) => string—Turns items into real `<a href>` links
renderLink(props: PaginationLinkProps) => ReactNode—Custom link component such as Next `Link` (with `getHref`)
size'sm' | 'md''md'Button size: 32 / 40px
showFirstLastbooleanfalseFirst / last page buttons
compactboolean | 'auto''auto'`‹ 3 / 24 ›` view; `auto` — below 640px
labels{ nav, previous, next, first, last, page, ellipsis, compact }—Screen reader texts (Uzbek by default)

PaginationInfo

Other <p> attributes are passed through.

PropTypeDefaultDescription
pagerequirednumber—Current page (1-based)
pageSizerequirednumber—Records per page
totalrequirednumber—Total records
labels{ template?: (from, to, total) => ReactNode; empty?: ReactNode }—Defaults: "21–30 / 230 ta" and "Ma'lumot yo'q"

Data attributes

AttributeWhereValue
data-sizenav, buttonssm · md
data-compactnavauto, or present (compact)
data-activepage buttonpresent on the current page
data-disabledprevious / nextpresent at the edges

Accessibility

  • Structure. <nav aria-label="Sahifalash"> wraps a <ul> list, so screen readers announce a navigation landmark and the number of items.
  • Current page has aria-current="page". Color is not the only signal: the current button is filled.
  • Names. Every button has an aria-label ("5-sahifa", "Keyingi sahifa"); icons are aria-hidden. The … is read as "Boshqa sahifalar".
  • Keyboard. Plain buttons or links: Tab to move, Enter to activate. Disabled edge buttons are skipped by Tab.
  • PaginationInfo uses aria-live="polite", so the new range is announced when the page changes.

On this page