* fix(app-router): align router autoscroll with Next
App Router navigation could diverge from Next.js router autoscroll semantics around hydration timing, loading-shell same-page search navigations, and routes whose first committed node is React-hoisted into head. That broke the upstream router-autoscroll deploy-suite because the client marked hydration too early, static loading-shell templates were not usable for navigation, and the fallback scroll path masked the old-handler hoisted-head behavior.
The fix moves the Next-compatible hydration marker into a passive effect, wires production hydrateRoot error callbacks to match Next's implicit root-boundary and recoverable-error handling, lets authoritative loading-shell templates drive static route optimistic payloads, and skips fallback document scrolling when the committed document only exposes a React-hoisted head resource. The ported tests cover the upstream router-autoscroll behavior plus focused unit boundaries for prefetch mode, optimistic routing, head-resource detection, and hydrateRoot callbacks.
* fix(app-router): hoisted-head scroll fallback is per-intent, not global head scan
* test(app-router): add full-chain scroll intent integration test
* fix(app-router): match legacy scroll focus parity
* fix(app-router): preserve latest scroll navigation intent
* fix(app-router): scope scroll and error recovery
* fix(app-router): gate navigation failure recovery
* fix(app-router): match navigation failure handling
* fix(app-router): align scroll completion parity
* fix(app-router): retain latest failure target
* fix(app-router): preserve route error boundaries
* fix(app-router): clear discarded failure targets
* fix(app-router): align failure recovery boundaries
* fix(app-router): disarm refused recovery targets
* fix(app-router): preserve failure target ownership
* fix(app-router): match boundary recovery parity
---------
Co-authored-by: James <james@eli.cx>
* fix(app-router): include parallel slot params in useParams
App Router navigation state previously used only the primary route match params when setting request navigation context, RSC param headers, render observations, and optimistic navigation payloads. That lost catch-all params from active parallel slot branches, so useParams() diverged from the slot page's params prop after SSR and client navigations.
Next.js derives params from the selected router tree across parallel route branches. Resolve active slot params from route metadata, merge them into the navigation params boundary, and feed that selected param set through server render and optimistic navigation payloads.
Port the upstream parallel-route-navigations fixture and cover the route graph, selected params helper, and optimistic payload boundary.
* fix(app-router): preserve canonical pathname in navigation context after middleware rewrite
* fix(app-router): resolve parallel slot params for static, revalidation, and action paths
* chore: retrigger CI
* fix(app-router): address ask-bonk review comments on parallel slot params
- Remove unused parameter from
- Add clarifying comment on routeParams fallback being deliberate no-op
- Use (full matched params) instead
of narrowed as base for slot param merge,
preserving interception-specific extras on ISR regeneration path
* fix(app-router): use full navigationParams in server-action re-render path
The server-action re-render path was passing narrowed
actionRerenderTarget.params to resolveAppPageNavigationParams,
dropping interception-specific extras from source-route intercepts.
Use actionRerenderTarget.navigationParams (full matched params) instead,
matching the pattern used in the dispatch ISR path at
app-page-dispatch.ts:602.
Adds unit tests for the intercepted-slot and mixed intercepted +
non-intercepted slot branches of resolveAppPageNavigationParams.
* docs(router): clarify decode invariant and type cast rationale in parallel-slot params
- app-optimistic-routing.ts: add comment explaining the pre-existing
double-decode asymmetry between route params and slot params, noting
TODO to converge decode passes
- app-server-action-execution.ts: expand the comment on the interceptOpts
cast to document why it's safe and what a follow-up would look like
* fix(app-router): merge slot params for fresh intercepts
---------
Co-authored-by: James <james@eli.cx>
App Router dynamic-route params were being returned in reversed key
order (deepest segment first) because the recursive trie matcher
mutated result.params on the way back up the call stack.
Fix by collecting param entries into a shared stack during forward
traversal, then materializing the params object from that stack at
the terminal node. This preserves declaration order for Object.keys().
- Shared buildParams helper extracted to routing/utils.ts to unify
code between route-trie.ts and app-optimistic-routing.ts.
- Added tests verifying key order for [category]/[id], [a]/[b]/[c],
mixed static+dynamic, and backtracking-to-catch-all scenarios.
Fixes#1537
* fix(app-router): render optimistic loading shells for dynamic navigation
Dynamic App Router links currently skip automatic prefetch for route patterns with params, so a navigation to a sibling dynamic URL waits for fresh RSC data before committing anything. That diverges from Next.js, where automatic prefetch learns the loading shell for dynamic routes and the router can commit the shell immediately while the full payload is fetched.
The client prefetch path now requests a loading-shell render for dynamic app routes, keeps that entry out of the navigation cache, learns reusable optimistic route templates from settled shell prefetches, and commits a detached optimistic payload for matching dynamic navigations. The server emits shell-only RSC payloads under a separate cache variant so normal navigation data is not polluted.
Tests cover matching semantics, shell-only page wiring, cache key isolation, prefetch cache consumption, Link prefetch behavior, and the upstream Next.js optimistic-routing deploy-suite case.
* chore(app-router): address optimistic routing review