npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@choice-ui/pagination

v0.0.3

Published

A pagination component for navigating through paginated data with page numbers and controls

Downloads

220

Readme

Pagination

An advanced pagination component with compound components for flexible layouts, supporting page navigation, page size selection, and direct page input.

Features

  • Compound Components: Flexible composition with Pagination.Spinner, Pagination.Navigation, and Pagination.ItemsPerPage
  • Smart Navigation: Intelligent page number display with ellipsis for large datasets
  • Page Size Control: Built-in items per page selector using Segmented component
  • Direct Page Jump: Input field for quick navigation to specific pages
  • Keyboard Support: Full keyboard navigation and shortcuts
  • Loading States: Built-in loading state support
  • Accessibility: ARIA labels and keyboard navigation
  • Customizable Labels: Support for custom text labels and internationalization
  • Performance: Optimized with useMemo for expensive calculations

Installation

npm install @choic-ui/react

Basic Usage

import { Pagination } from "@choic-ui/react"

function App() {
  const [currentPage, setCurrentPage] = useState(1)
  const [itemsPerPage, setItemsPerPage] = useState(10)

  return (
    <Pagination
      currentPage={currentPage}
      totalItems={250}
      itemsPerPage={itemsPerPage}
      onPageChange={setCurrentPage}
      onItemsPerPageChange={setItemsPerPage}
    >
      <Pagination.Spinner />
      <Pagination.Navigation />
      <Pagination.ItemsPerPage />
    </Pagination>
  )
}

Compound Components

Pagination.Spinner

Page input with arrow navigation buttons. Allows direct page number input with automatic validation.

<Pagination.Spinner />

Features:

  • Left/right arrow buttons for previous/next navigation
  • Click to edit page number directly
  • Enter key to submit, Escape to cancel
  • Automatic text selection on focus
  • Input validation with min/max boundaries
  • Shows current page and total pages

Pagination.Navigation

Number button navigation with intelligent ellipsis display.

<Pagination.Navigation variant={(isActive) => (isActive ? "solid" : "ghost")} />

Features:

  • Smart page number display with ellipsis
  • Active page highlighting
  • Customizable button variants
  • Responsive to maxPageButtons prop
  • Keyboard accessible

Pagination.ItemsPerPage

Page size selector using the Segmented component.

<Pagination.ItemsPerPage options={[10, 25, 50, 100]} />

Features:

  • Segmented control for size selection
  • Custom options support
  • Automatic page recalculation on size change
  • Can be rendered as custom child component

API Reference

Pagination (Root)

| Prop | Type | Default | Description | | ---------------------- | -------------------------------- | ----------------------- | ----------------------------------------- | | currentPage | number | - | Current active page (1-indexed) | | totalItems | number | - | Total number of items | | itemsPerPage | number | 10 | Number of items per page | | onPageChange | (page: number) => void | - | Callback when page changes | | onItemsPerPageChange | (itemsPerPage: number) => void | - | Callback when items per page changes | | maxPageButtons | number | 7 | Maximum number of page buttons to display | | pageSizeOptions | number[] | [10, 20, 30, 50, 100] | Available page size options | | disabled | boolean | false | Disable all interactions | | loading | boolean | false | Show loading state | | labels | Partial<PaginationLabels> | - | Custom text labels | | showPageSizeSelector | boolean | true | Show page size selector |

PaginationLabels

interface PaginationLabels {
  page: string // Default: "Page"
}

Examples

Basic Pagination

<Pagination
  currentPage={currentPage}
  totalItems={100}
  onPageChange={setCurrentPage}
>
  <Pagination.Navigation />
</Pagination>

Full Featured

<Pagination
  currentPage={currentPage}
  totalItems={500}
  itemsPerPage={itemsPerPage}
  onPageChange={setCurrentPage}
  onItemsPerPageChange={setItemsPerPage}
>
  <Pagination.Spinner />
  <Pagination.Navigation />
  <Pagination.ItemsPerPage />
</Pagination>

Custom Layout

<Pagination
  currentPage={currentPage}
  totalItems={1000}
  onPageChange={setCurrentPage}
>
  <div className="flex w-full justify-between">
    <Pagination.ItemsPerPage />
    <Pagination.Navigation />
    <Pagination.Spinner />
  </div>
</Pagination>

With Custom Labels (i18n)

<Pagination
  currentPage={currentPage}
  totalItems={250}
  onPageChange={setCurrentPage}
  labels={{
    first: "�u",
    last: "+u",
    previous: "
�u",
    next: "�u",
    page: "u",
    of: "q",
    items: "a",
    itemsPerPage: "a/u"
  }}
>
  <Pagination.Navigation />
</Pagination>

Disabled State

<Pagination
  currentPage={1}
  totalItems={100}
  disabled={true}
  onPageChange={setCurrentPage}
>
  <Pagination.Navigation />
</Pagination>

Loading State

<Pagination
  currentPage={currentPage}
  totalItems={totalItems}
  loading={isLoading}
  onPageChange={setCurrentPage}
>
  <Pagination.Navigation />
</Pagination>

Custom Page Size Options

<Pagination
  currentPage={currentPage}
  totalItems={1000}
  itemsPerPage={25}
  pageSizeOptions={[25, 50, 100, 200]}
  onPageChange={setCurrentPage}
  onItemsPerPageChange={setItemsPerPage}
>
  <Pagination.ItemsPerPage />
  <Pagination.Navigation />
</Pagination>

Minimal Navigation

<Pagination
  currentPage={currentPage}
  totalItems={50}
  maxPageButtons={5}
  onPageChange={setCurrentPage}
>
  <Pagination.Navigation />
</Pagination>

Custom Navigation Variants

<Pagination
  currentPage={currentPage}
  totalItems={200}
  onPageChange={setCurrentPage}
>
  <Pagination.Navigation variant={(isActive) => (isActive ? "primary" : "secondary")} />
</Pagination>

Server-Side Pagination

function TableWithPagination() {
  const [page, setPage] = useState(1)
  const [pageSize, setPageSize] = useState(20)

  const { data, isLoading } = useQuery({
    queryKey: ["items", page, pageSize],
    queryFn: () => fetchItems({ page, pageSize }),
  })

  return (
    <>
      <Table data={data?.items} />
      <Pagination
        currentPage={page}
        totalItems={data?.total || 0}
        itemsPerPage={pageSize}
        loading={isLoading}
        onPageChange={setPage}
        onItemsPerPageChange={setPageSize}
      >
        <Pagination.Spinner />
        <Pagination.Navigation />
        <Pagination.ItemsPerPage />
      </Pagination>
    </>
  )
}

URL State Synchronization

function PaginatedList() {
  const [searchParams, setSearchParams] = useSearchParams()
  const page = parseInt(searchParams.get("page") || "1")
  const pageSize = parseInt(searchParams.get("pageSize") || "20")

  const handlePageChange = (newPage: number) => {
    setSearchParams((prev) => {
      prev.set("page", newPage.toString())
      return prev
    })
  }

  const handlePageSizeChange = (newPageSize: number) => {
    setSearchParams((prev) => {
      prev.set("pageSize", newPageSize.toString())
      prev.set("page", "1") // Reset to first page
      return prev
    })
  }

  return (
    <Pagination
      currentPage={page}
      totalItems={1000}
      itemsPerPage={pageSize}
      onPageChange={handlePageChange}
      onItemsPerPageChange={handlePageSizeChange}
    >
      <Pagination.Navigation />
      <Pagination.ItemsPerPage />
    </Pagination>
  )
}

Keyboard Shortcuts

Pagination.Spinner

  • Click - Enter edit mode
  • Enter - Submit page number
  • Escape - Cancel editing
  • Arrow Left/Right - Navigate pages (via buttons)

Pagination.Navigation

  • Tab - Navigate between page buttons
  • Enter/Space - Select page

Styling

The component uses Tailwind Variants for styling. Key style configurations:

// Size variants
size: {
  default: "text-body",
  large: "text-body-medium"
}

// Button variants (Navigation)
variant: (isActive: boolean) => isActive ? "solid" : "ghost"

// Spinner styles
- Arrow buttons with rounded corners
- Input field with border highlight on focus
- Seamless integration with button group

Accessibility

  • ARIA Labels: All interactive elements have proper ARIA labels
  • Keyboard Navigation: Full keyboard support for all interactions
  • Focus Management: Proper focus states and tab order
  • Screen Reader Support: Announces page changes and current state
  • aria-current: Active page marked with aria-current="page"
  • Disabled States: Proper disabled state handling for boundaries

Architecture

File Structure

pagination/
├── components/
│   ├── pagination-root.tsx       # Root compound component
│   ├── pagination-spinner.tsx    # Page input with arrows
│   ├── pagination-navigation.tsx # Number buttons
│   ├── pagination-items-per-page.tsx # Page size selector
│   └── pagination-context.tsx    # Shared context provider
├── hooks/
│   └── use-pagination.ts         # Core pagination logic
├── utils/
│   └── pagination-helpers.ts     # Utility functions
├── types.ts                      # TypeScript definitions
├── tv.ts                         # Tailwind Variants styles
├── pagination.tsx                # Main export
└── index.ts                      # Public exports

Design Patterns

  1. Compound Components: Flexible composition pattern
  2. Context API: Shared state between sub-components
  3. Custom Hooks: Reusable pagination logic
  4. Tailwind Variants: Type-safe styling system
  5. Performance Optimization: Memoized calculations

Best Practices

  1. Always provide totalItems: Required for calculating total pages
  2. Controlled component: Always handle onPageChange callback
  3. Page size changes: Handle onItemsPerPageChange when using ItemsPerPage
  4. Loading states: Use loading prop during data fetching
  5. Internationalization: Provide custom labels for non-English interfaces
  6. Responsive design: Consider using compact mode for mobile layouts
  7. URL synchronization: Keep pagination state in URL for shareable links
  8. Debouncing: Consider debouncing rapid page changes for server requests

Browser Support

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+
  • Opera 76+

Related Components