Question presented to candidate: "We have a product list page with a search box, category filters and pagination. Product wants every filtered view to be shareable as a link and to survive a refresh. How would you build that in the App Router?"
What a strong answer should cover:
- The URL is the state:
?q=lamp&category=desks&page=2is the single source of truth, so links, refresh, back/forward and sharing all work for free. - The page (a Server Component) reads the
searchParamsprop (a Promise since Next.js 15), validates it, and queries the data on the server; the route becomes dynamic. - A small Client Component writes to the URL with
useSearchParams+useRouter().replace()(debounced), wrapped in<Suspense>; pagination and filters can be plain<Link>s with a built query string. - Treat search params as untrusted input: arrays (
string | string[]), invalid numbers, unknown values, out-of-range pages; resetpagewhen the query changes. replacevspush(history entries),useTransitionfor pending UI, and that each URL change re-renders the page on the server.
Clarifying questions expected:
- "Should typing in the search box create back-button history entries, or only page changes?" β decides
replacevspush. - "How big is the dataset?" β decides server-side filtering (searchParams prop) vs filtering an already-loaded list in the client.
Code / implementation expected: Yes β a Server Component page that parses searchParams, a debounced client search box, and pagination links.