React - List View with Pagination
Overview
Estimated time: 20–30 minutes
Render a paginated list with accessible controls, preserving the current page in the hash.
Try it: Paginated list
View source
function usePage(total, perPage){
const [page, setPage] = React.useState(() => Number(new URLSearchParams((location.hash.split('?')[1]||'')).get('page')) || 1);
React.useEffect(() => {
const params = new URLSearchParams(location.hash.split('?')[1]||'');
params.set('page', page);
location.hash = `#/list?page=${params.get('page')}`;
}, [page]);
const pages = Math.ceil(total/perPage);
const go = p => setPage(Math.max(1, Math.min(pages, p)));
return {page, setPage:go, pages};
}
function Pager({page, pages, onChange}){
const ids = Array.from({length:pages}, (_,i)=>i+1);
return (
<nav aria-label="Pagination">
<button onClick={()=> onChange(page-1)} disabled={page===1}>Prev</button>
{ids.map(p => (
<button key={p} aria-current={p===page? 'page': undefined} onClick={()=> onChange(p)}>{p}</button>
))}
<button onClick={()=> onChange(page+1)} disabled={page===pages}>Next</button>
</nav>
);
}
function App(){
const items = Array.from({length:100}, (_,i)=> `Item ${i+1}`);
const perPage = 10;
const {page, setPage, pages} = usePage(items.length, perPage);
const start = (page-1)*perPage;
const slice = items.slice(start, start+perPage);
return (<div>
<ul>{slice.map(x => <li key={x}>{x}</li>)}</ul>
<Pager page={page} pages={pages} onChange={setPage} />
</div>);
}
ReactDOM.createRoot(document.getElementById('try-list-pag')).render(<App />);
Syntax primer
- Keep page in URL to preserve state on reload and share links.
- Use aria-current="page" to mark the active page.
Common pitfalls
- Resetting to page 1 after navigation—keep it stable across views when appropriate.
Exercises
- Add input to jump to a page number with validation.
- Disable buttons when the page is at the bounds.
Checks for Understanding
- Why keep the current page in the URL/hash?
- How do you mark the active page button for assistive technologies?
- How do you guard against navigating outside valid page bounds?
Answers
- It preserves state on reload and makes the paginated view shareable via a link.
- Apply aria-current="page" on the active page button/link.
- Clamp the target page between 1 and pages when changing pages.