Pagination

URL-driven page state via searchParams. The loader slices the dataset by ?page=N; links navigate the browser, no client JS. Refresh, share, and back/forward all work because the page number lives in the URL.

Tip
Every ?page=N URL is bookmarkable and crawlable — unlike client-side state. Combine server pagination with infinite scroll by pre-rendering page 1 then fetching subsequent pages via the same URL.

Rows 41–47 of 47

  • #41 · Item 41
  • #42 · Item 42
  • #43 · Item 43
  • #44 · Item 44
  • #45 · Item 45
  • #46 · Item 46
  • #47 · Item 47
src/app/examples/pagination/page.tsxTSX
// Page reads ?page from searchParams; loader returns the slice +
// pagination metadata. Links carry ?page=N — no client JS, no
// state. Browser back/forward Just Works.

export async function loader({ request }) {
  const url = new URL(request.url);
  const page = Math.max(1, parseInt(url.searchParams.get("page") ?? "1", 10));
  const start = (page - 1) * PAGE_SIZE;
  return {
    rows: items.slice(start, start + PAGE_SIZE),
    page,
    totalPages: Math.ceil(items.length / PAGE_SIZE),
  };
}

// Render: <a href={"?page=" + (page - 1)}>← prev</a>
Try it liveOpen in play

A runnable project for this example, opened in the bext play editor — edit the code and the preview updates.

src/app/page.tsx1 file · runs in your browser
Live-editable — the preview recompiles as you type.