Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix router.query losing dynamic params with middleware#89977

Open
VrajKD wants to merge 2 commits into
vercel:canaryfrom
VrajKD:fix/router-query-middleware-params
Open

Fix router.query losing dynamic params with middleware#89977
VrajKD wants to merge 2 commits into
vercel:canaryfrom
VrajKD:fix/router-query-middleware-params

Conversation

@VrajKD

@VrajKD VrajKD commented Feb 13, 2026

Copy link
Copy Markdown

Summary

Fixes #54077

When middleware is active (even a no-op NextResponse.next()), router.query loses dynamic route parameters during client-side navigation in production builds.

Root cause: After getRouteInfo() processes middleware, the change() method re-extracts dynamic params using routeInfo.resolvedAs (the middleware-processed path). This path may not match the route pattern (e.g., /users/[userId]/test), so getRouteMatcher returns false and the params are dropped from query.

Fix: When the middleware-resolved path doesn't match the route pattern, fall back to extracting params from the original as URL — the URL the user intended to navigate to, which always matches the route pattern.

Reproduction

  1. Create a Pages Router app with a dynamic route (/users/[userId]/test)
  2. Add any middleware (even empty NextResponse.next())
  3. Navigate to /users/user1/test
  4. Call router.push({ pathname: router.pathname, query: { ...router.query, step: '1' } })
  5. In production: router.query loses userId, URL shows /users/[userId]/test?step=1

Test plan

  • New e2e test: test/e2e/middleware-query-params-navigation/ — verifies dynamic params survive router.push with spread router.query when middleware is active
  • Passes in both production (test-start-turbo) and development (test-dev-turbo) modes
  • Existing middleware dynamic route tests pass (12/12 in middleware-general)
  • Existing middleware rewrite tests pass (59/59 in middleware-rewrites)
  • 2 pre-existing failures in middleware-general (shallow linking) confirmed unrelated — fail on canary without this change

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.
@nextjs-bot

Copy link
Copy Markdown
Contributor

Allow CI Workflow Run

  • approve CI run for commit: 211572f

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

1 similar comment
@nextjs-bot

Copy link
Copy Markdown
Contributor

Allow CI Workflow Run

  • approve CI run for commit: 211572f

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@VrajKD

VrajKD commented May 13, 2026

Copy link
Copy Markdown
Author

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 change() when resolvedAs doesn't match the route pattern). Would love your eyes on it if you get a chance!

@unstubbable

Copy link
Copy Markdown
Contributor

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 router.ts. Same result with the original ferenckv/next-router-bug setup. Looks like #54077 got fixed by something unrelated in the years since, so I dropped the "fixes #54077" line from my PR description since it was misleading.

#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 getMiddlewareData(), not in the place where you're adding the fallback. With a catch-all, routeInfo.resolvedAs ends up holding the data URL, the catch-all regex matches the multi-segment input, and router.query.<param> gets overwritten with the data-URL segments. Single-segment dynamic routes don't show it because their regex rejects the multi-segment input.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

router.query does not contain path parameters if a middleware is used

3 participants