Fix router.query losing dynamic params with middleware#89977
Conversation
When middleware is active (even a no-op NextResponse.next()), client-side navigation via router.push loses dynamic route parameters from router.query in production builds. This happens because the post-middleware parameter re-extraction uses routeInfo.resolvedAs (the middleware-processed path) which may not match the route pattern, causing getRouteMatcher to return false and params to be dropped. Add a fallback: when the middleware-resolved path doesn't match the route pattern, try extracting params from the original `as` URL instead.
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
1 similar comment
|
Allow CI Workflow Run
Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer |
|
Hey @unstubbable — saw your fix in #93294, looks like we're both tackling #54077. This PR is more of a downstream safety net (fallback in |
|
Hey @VrajKD, I tried your fixture against the latest canary and it passes in all three modes I tested (turbopack-prod, webpack-prod, turbopack-dev) without any change to #93294 isn't really overlapping with this. It's for a different code path. The scenario I hit needs basePath + a rewrite + middleware + a catch-all dynamic route, and the corruption is upstream in Mind double-checking whether your fixture still fails for you on canary? If it doesn't, the fallback here might not be load-bearing anymore. |
Summary
Fixes #54077
When middleware is active (even a no-op
NextResponse.next()),router.queryloses dynamic route parameters during client-side navigation in production builds.Root cause: After
getRouteInfo()processes middleware, thechange()method re-extracts dynamic params usingrouteInfo.resolvedAs(the middleware-processed path). This path may not match the route pattern (e.g.,/users/[userId]/test), sogetRouteMatcherreturnsfalseand the params are dropped fromquery.Fix: When the middleware-resolved path doesn't match the route pattern, fall back to extracting params from the original
asURL — the URL the user intended to navigate to, which always matches the route pattern.Reproduction
/users/[userId]/test)NextResponse.next())/users/user1/testrouter.push({ pathname: router.pathname, query: { ...router.query, step: '1' } })router.querylosesuserId, URL shows/users/[userId]/test?step=1Test plan
test/e2e/middleware-query-params-navigation/— verifies dynamic params surviverouter.pushwith spreadrouter.querywhen middleware is activetest-start-turbo) and development (test-dev-turbo) modesmiddleware-general)middleware-rewrites)middleware-general(shallow linking) confirmed unrelated — fail oncanarywithout this change