Symptom
A client-side filter writes ?show=waiting with replaceState('/home?show=waiting', $page.state) from $app/navigation, and a reactive $: view = parse($page.url.searchParams) never changes. The address bar shows the new URL; the store does not.
Cause
Verified in @sveltejs/kit 2.x src/runtime/client/client.js (replaceState, ~line 2551; pushState ~2504): both call history.replaceState/pushState(opts, '', resolve_url(url)), then set page.state = state and re-render the root with clone_page(page). page.url is only reassigned by update_url() inside real navigations. Shallow routing intentionally keeps page.url pinned to the last navigated URL; the new location is stored in history state under PAGE_URL_KEY.
Fix
- Keep the UI state as a local variable, initialized once from
$page.url.searchParams(the server renders with the same URL, so first paint already reflects a shared/reloaded link; no flicker). - On user action: mutate the local state, then
replaceState(path + search, $page.state)as a one-way mirror. - Don't use
goto(url, { replaceState: true })for this if the page has a serverload: it re-runsload(network round trip) on every tap. replaceStatethrows in dev before the router starts ("Cannot call replaceState(...) before router is initialized"); calling it from a click handler is safe, fromonMounton a hard load it needs a macrotask (setTimeout).
Applies to SvelteKit 2 shallow routing (pushState/replaceState from $app/navigation).