Course outline · 0% complete

0/29 lessons0%

Course overview →

searchParams: filters and pagination in the URL

lesson 5-4 · ~10 min · 18/29

State that belongs in the URL

Dynamic segments identify which thing a page shows (/blog/hello-world). But search results, filters, and page numbers are a different kind of state: how to show a listing. The web's convention for that is the query string — the part of a URL after ?, made of key=value pairs:

/search?q=shoes&page=2

Putting this state in the URL instead of useState is what makes results shareable (paste the link, your teammate sees the same filtered view), bookmarkable, and back-button friendly. Every serious store, search engine, and admin table works this way, and you will build one within weeks of writing Next.js at work.

A page component receives the parsed query string as the searchParams prop — and like params in this unit, current Next.js makes it a Promise you must await:

// app/search/page.js
export default async function SearchPage({ searchParams }) {
  const { q, page } = await searchParams;
  const results = await findProducts(q, Number(page ?? 1));
  return <h1>{results.length} results for "{q}"</h1>;
}

Two consequences to decode

  • Every value is a string (or missing). The URL is text, so page arrives as "2", not 2, and is undefined when the visitor omits it. Defensive parsing with a default is not optional — users hand-edit URLs.
  • Reading searchParams makes the page dynamic. The server cannot know ?q=shoes at build time, so the page renders per request. (Unit 7 turns this into a general rule.) Dynamic segments differ here: generateStaticParams can list slugs ahead of time, but nobody can list every possible query.

Linking into a filtered view is just a query string in href:

<Link href="/search?q=shoes&page=2">Next page</Link>

Code exercise · javascript

Harden the pagination parsing (pure JavaScript, runnable here). Write pageNumber(searchParams): given an object like { page: "3" }, return the page as an integer. Default to 1 when page is missing, not a whole number, or less than 1 — users type anything into URLs.

Quiz

Why put a product list's filters in the query string (?category=lamps&sort=price) instead of a useState hook?

Problem

A page reads searchParams to show ?q= search results. At build time, can next build prerender it as static HTML, yes or no?