SvPagination

A standalone pager: page numbers with an ellipsis, plus prev/next and optional first/last controls.

SvPagination is a controlled component - you own the current page and update it from onChange. It pairs with the grid, a card list, or any paged data. The sequence of page buttons (boundaries, a window of siblings around the current page, and collapsed gaps) is produced by the pure paginationRange helper, so the layout math is testable on its own and reusable in a custom pager. Colors come from the --sg-* tokens.

Basic usage

<script lang="ts">
  import { SvPagination } from '@svgrid/grid'
  let page = $state(1)
</script>

<SvPagination page={page} pageCount={20} onChange={(n) => (page = n)} showFirstLast />

Props

Prop Type Default Description
page number - 1-based current page. Controlled - you update it.
pageCount number - Total number of pages.
onChange (page: number) => void - Fires with the clamped target page on any navigation.
siblingCount number 1 Pages shown on each side of the current page.
boundaryCount number 1 Pages pinned at each end of the range.
showFirstLast boolean false Show the jump-to-first and jump-to-last controls.
showPrevNext boolean true Show the previous and next controls.
size sm | md | lg md Button size, shared with the rest of the kit.
disabled boolean false Dims and blocks every control.
ariaLabel string Pagination Accessible name for the <nav> landmark.

The pure paginationRange({ page, pageCount, siblingCount?, boundaryCount? }) helper (from @svgrid/grid) returns the (number | 'ellipsis-left' | 'ellipsis-right')[] sequence and is exported so you can build a custom-styled pager on the same math.

Patterns

Driving the grid

Keep the grid's page and the pager in one piece of state:

<SvGrid {columns} {rows} bind:page pageSize={25} />
<SvPagination {page} pageCount={Math.ceil(total / 25)} onChange={(n) => (page = n)} />

A custom pager on the same math

Reuse paginationRange when you want your own markup:

<script lang="ts">
  import { paginationRange } from '@svgrid/grid'
  const items = $derived(paginationRange({ page, pageCount: 40, siblingCount: 2 }))
</script>

{#each items as it}
  {#if typeof it === 'number'}<button onclick={() => go(it)}>{it}</button>
  {:else}<span>…</span>{/if}
{/each}

Compact vs wide

Raise siblingCount / boundaryCount for wide layouts, or drop showPrevNext and set size="sm" for a tight inline pager.

Server-side pagination

When the server owns the data, fetch one page at a time and let the pager report the total. Keep page as state, refetch in an $effect, and derive pageCount from the row count the API returns:

<script lang="ts">
  import { SvGrid, SvPagination } from '@svgrid/grid'

  let page = $state(1)
  const pageSize = 25
  let rows = $state<Order[]>([])
  let total = $state(0)

  async function load(p: number) {
    const res = await fetch(`/api/orders?page=${p}&size=${pageSize}`)
    const data = await res.json()
    rows = data.items
    total = data.total
  }

  $effect(() => { load(page) })
  const pageCount = $derived(Math.max(1, Math.ceil(total / pageSize)))
</script>

<SvGrid {columns} {rows} />
<SvPagination {page} {pageCount} showFirstLast onChange={(n) => (page = n)} />

Tip: onChange clamps the target into [1, pageCount] and only fires when it actually differs from the current page, so clicking Next on the last page or Prev on the first is a no-op - you never issue a redundant or out-of-range fetch.

Accessibility

See also