Search results that can't be shared via URL are functionally private — users cannot bookmark a filtered view, send a result set to a colleague, or return to the same context after closing a tab. This violates ISO 25010:2011 functional-suitability and is a first-order UX failure for any directory product where sharing and referral are core growth mechanics. Non-shareable URLs also break SEO: search engines cannot index filtered result pages, eliminating long-tail keyword traffic for category, location, and attribute combinations that represent the directory's core value.
High because non-shareable URLs eliminate bookmark, referral, and SEO surface area — all of which are foundational to a directory's discovery and growth model.
Serialize all search state (query, filters, sort, page) into URL parameters and read them back on load. In your listings API route:
// /api/listings
const q = req.nextUrl.searchParams.get('q')
const category = req.nextUrl.searchParams.get('category')
const page = parseInt(req.nextUrl.searchParams.get('page') || '1')
const listings = await db.listings.findMany({
where: { name: { contains: q }, category },
orderBy: [{ rating: 'desc' }, { createdAt: 'desc' }],
skip: (page - 1) * 20,
take: 20
})
On the client, use router.push or useSearchParams to keep the URL in sync with every filter change. Verify by copying the URL, opening a new tab, and confirming identical results.
ID: directory-search-discovery.pagination-url.shareable-urls
Severity: high
What to look for: Enumerate all relevant files and Perform a search with multiple filters and navigate to a specific results page. Copy the current URL. Open a new tab, paste the URL, and load the page. Verify that the results, filters, and page number are identical to what you saw in the original tab.
Pass criteria: At least 1 conforming pattern must exist. When the same URL is loaded, the identical search state, filters, and results are displayed. No randomness or variance in the results.
Fail criteria: URL is copied but loading it in a new tab shows different results, missing filters, or wrong page.
Skip (N/A) when: Search/filtering not implemented. Signal: no search input or filters found.
Detail on fail: "URL preserves filters and page, but results are randomized or ordered differently on page load." or "URL lost query parameters when copied."
Remediation: Ensure your API endpoint is deterministic for the same parameters:
// /api/listings (API route)
export async function GET(req) {
const q = req.nextUrl.searchParams.get('q')
const category = req.nextUrl.searchParams.get('category')
const page = parseInt(req.nextUrl.searchParams.get('page') || '1')
const pageSize = 20
// Fetch with consistent ordering
const listings = await db.listings.findMany({
where: {
name: { contains: q },
category
},
orderBy: [{ rating: 'desc' }, { createdAt: 'desc' }],
skip: (page - 1) * pageSize,
take: pageSize
})
return Response.json({ listings })
}
Ensure the database query is deterministic (use orderBy clauses consistently). Test by loading the same URL multiple times and verifying identical results.