Go REST

Build a paginated user list in React

A working component that fetches a page, renders rows, and uses X-Pagination-Pages to render Previous/Next correctly.

Pagination on a list endpoint is one of the most common patterns in any UI, and also one of the most common places to get details wrong. This recipe builds it incrementally: a working Previous/Next first, then URL sync so deep links work, then a page-jump component for long lists. Every step uses theX-Pagination-* headers Go REST returns.

The headers you need

Every list response from Go REST includes:

ReadX-Pagination-Pages on the response and use it to disable Next when you are on the last page. Without it, you have to guess, and guessing wrong means a "Next" button that returns an empty list.

The minimal component

One state for the current page, one for the rows, one for the total. Every page change re-fetches.

import { useState, useEffect } from "react";

function UserList() {
  const [page, setPage]   = useState(1);
  const [users, setUsers] = useState([]);
  const [total, setTotal] = useState(1);
  const [busy, setBusy]   = useState(false);

  useEffect(() => {
    setBusy(true);
    fetch(`https://gorest.co.in/public/v2/users?page=${page}`)
      .then(async (res) => {
        setTotal(parseInt(res.headers.get("x-pagination-pages") || "1", 10));
        return res.json();
      })
      .then(setUsers)
      .finally(() => setBusy(false));
  }, [page]);

  return (
    <div>
      <table>
        <thead>
          <tr><th>Name</th><th>Email</th><th>Status</th></tr>
        </thead>
        <tbody>
          {users.map(u => (
            <tr key={u.id}>
              <td>{u.name}</td>
              <td>{u.email}</td>
              <td>{u.status}</td>
            </tr>
          ))}
        </tbody>
      </table>

      <nav className="pagination">
        <button onClick={() => setPage(p => p - 1)} disabled={page <= 1 || busy}>
          Previous
        </button>
        <span>Page {page} of {total}</span>
        <button onClick={() => setPage(p => p + 1)} disabled={page >= total || busy}>
          Next
        </button>
      </nav>
    </div>
  );
}

A few details worth pointing out:

Sync to the URL

Pagination state belongs in the URL so users can copy a link, refresh, or hit the back button to return to a specific page. With React Router:

import { useSearchParams } from "react-router-dom";

function UserList() {
  const [params, setParams] = useSearchParams();
  const page = parseInt(params.get("page") || "1", 10);

  function setPage(n) {
    setParams({ page: String(n) });
  }
  // ...same fetch as before, keyed on `page`
}

The fetch effect now keys onpage derived fromparams. Hitting "Next" updates the URL, which updatespage, which triggers the effect, which fetches the next page. URL is the single source of truth.

Page-jump component

For lists with more than ~10 pages, Previous/Next is not enough; users want to jump to page 47 or skim the page count. A page-jump component renders a sliding window around the current page.

function PageJump({ page, total, setPage }) {
  const visible = pageWindow(page, total, 5); // [1, 2, 3, 4, 5]

  return (
    <ul className="pagination">
      {visible.map(n => (
        <li key={n}>
          <button
            aria-current={n === page ? "page" : undefined}
            onClick={() => setPage(n)}
            disabled={n === page}>
            {n}
          </button>
        </li>
      ))}
    </ul>
  );
}

function pageWindow(current, total, size) {
  const half = Math.floor(size / 2);
  let start = Math.max(1, current - half);
  let end   = Math.min(total, start + size - 1);
  start = Math.max(1, end - size + 1);
  return Array.from({ length: end - start + 1 }, (_, i) => start + i);
}

ThepageWindow helper centres a 5-page window around the current page, clamped to the valid range. Page 1 shows, page 50 of 100 shows, and the last page shows the last 5.

Filtering and pagination together

When the user changes a filter (status, search term), reset to page 1; otherwise they end up on page 47 of a filtered list that only has 3 pages. Make filter changes go through a single state setter that resets page:

function setFilter(next) { setStatus(next); setPage(1); }

Pre-fetch the next page

A nice polish: when the user is on page 3, optimistically fetch page 4 in the background so clicking Next feels instant. Use a separateuseEffect that fires whenpage is stable; cache the result keyed on page number.

Common mistakes

More recipes

Keep building

All recipes Integration guides