hard+240 XP
Build pagination with ellipsis
Build pagination for a fixed 10 pages. Printing all 10 page buttons at once doesn't scale — collapse the middle into an ellipsis instead, always keeping the first page, the last page, and the pages immediately around the current one visible.
Prev/Next move one page at a time and disable themselves at the start and end.
Requirements
- A `data-testid="current-page"` element shows the active page number as plain text, starting at 1.
- A `data-testid="prev"` and `data-testid="next"` button each; `prev` is `disabled` on page 1, `next` is `disabled` on page 10.
- Each visible page number is a button `data-testid="page-N"` (e.g. `page-1`, `page-10`).
- Skipped ranges render as `data-testid="ellipsis"` — with 10 total pages, not every page number can be on screen at once.
- Clicking any visible page button jumps straight to that page.
Your workspace
import { useState } from "react"; const TOTAL_PAGES = 10; export default function App() { const [page, setPage] = useState(1); // TODO: for 10 pages this renders every button — implement windowing // instead: always show page 1 and page 10, the current page and its // immediate neighbors, and a "..." for whatever is skipped in between. const pageNumbers = Array.from({ length: TOTAL_PAGES }, (_, i) => i + 1); return ( <div style={{ fontFamily: "sans-serif", padding: 16 }}> <p>Page: <span data-testid="current-page">{page}</span></p> <nav data-testid="pagination" style={{ display: "flex", gap: 6, flexWrap: "wrap" }}> <button data-testid="prev" disabled={page === 1} onClick={() => setPage((p) => Math.max(1, p - 1))} > Prev </button> {pageNumbers.map((n) => ( <button key={n} data-testid={`page-${n}`} aria-current={n === page ? "page" : undefined} onClick={() => setPage(n)} style={{ fontWeight: n === page ? "bold" : "normal" }} > {n} </button> ))} <button data-testid="next" disabled={page === TOTAL_PAGES} onClick={() => setPage((p) => Math.min(TOTAL_PAGES, p + 1))} > Next </button> </nav> </div> ); }
Ready to check it?
5 tests run against your rendered component, right here in your browser. Sign in to claim the XP when you pass.
Stuck? The react course covers the ideas this problem is built on.