Skip to content

fix(router): useRouter().push/replace/navigate do SPA navigation (no full reload) - #3075

Merged
kojiwakayama merged 3 commits into
mainfrom
fix/router-push-spa-navigation
Jul 24, 2026
Merged

fix(router): useRouter().push/replace/navigate do SPA navigation (no full reload)#3075
kojiwakayama merged 3 commits into
mainfrom
fix/router-push-spa-navigation

Conversation

@mattboon

Copy link
Copy Markdown
Collaborator

What

useRouter().push() / replace() / navigate() triggered a full document reload (destroying the JS realm) in both the app-router and pages-router, while <Link> correctly stayed SPA.

The hydration runtime wires <Link> clicks to its navigateSPA, but never registered a navigator against the shared navigation store that RouterProvider reads — so the store's navigate fell back to location.assign(href). This registers navigateSPA as the store's navigator (mapping history: 'push' | 'replace'), so programmatic navigation is SPA too. getNavigationStore is exported from the react runtime and imported by both the dev (dev-client-renderer) and prod (prod-scripts) hydration-module builders so the generated runtime can reach it.

How to test

  1. Load a page that stamps a marker on mount (e.g. window.__spa = Date.now()), then trigger useRouter().push('/other').
  2. Expect: the marker survives, URL + content update, no document reload — in both app and pages routers (previously the marker was wiped by a full reload).
  3. <Link> navigation is unchanged (still SPA).
  4. deno test src/html/hydration-script-builder/ — new router-push-spa.test.ts (red→green); all 12 files green.

Refs veryfront/veryfront-issue-inbox#200 (finding 7)

…navigator

The hydration runtime wired <Link> clicks to navigateSPA but never registered a
navigator against the shared navigation store that RouterProvider reads, so
useRouter().push()/replace()/navigate() fell through to location.assign() — a full
document reload — in both the app and pages routers, while <Link> stayed SPA.
Register navigateSPA as the store's navigator so programmatic navigation is SPA
too. Export getNavigationStore from the react runtime and import it in both the
dev (dev-client-renderer) and prod (prod-scripts) hydration module builders so the
generated runtime can reach it.

Refs veryfront/veryfront-issue-inbox#200 (finding 7)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf2abfdb06

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/html/hydration-script-builder/templates/router.ts Outdated
mattboon and others added 2 commits July 24, 2026 20:06
…est-typecheck gate

The lint:test-typecheck baseline runs deno check on test files; RuntimeHandle.win
omitted `location`, so `runtime.win.location.pathname = …` was a TS2339. Widen the
type. Behavior unchanged (test still green).
The SPA navigator previously used one boolean for both replace and history-free navigation. Carry an explicit history mode so push, replace, and popstate paths preserve their distinct browser-history contracts.

Constraint: useRouter replace must stay within the SPA while updating the visible URL.

Rejected: Treat replace as history-free navigation | leaves the rendered route and browser URL out of sync.

Confidence: high

Scope-risk: narrow

Tested: Generated router runtime regression; format; lint; typecheck

Not-tested: Browser e2e navigation suite
@kojiwakayama
kojiwakayama enabled auto-merge July 24, 2026 18:31
@kojiwakayama
kojiwakayama added this pull request to the merge queue Jul 24, 2026
Merged via the queue into main with commit ae4ed41 Jul 24, 2026
28 checks passed
@kojiwakayama
kojiwakayama deleted the fix/router-push-spa-navigation branch July 24, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants