Skip to content

fix(pages-router): preserve query string in Link and router.push#1572

Draft
james-elicx wants to merge 1 commit into
mainfrom
fix/pages-link-preserve-query
Draft

fix(pages-router): preserve query string in Link and router.push#1572
james-elicx wants to merge 1 commit into
mainfrom
fix/pages-link-preserve-query

Conversation

@james-elicx
Copy link
Copy Markdown
Member

Closes #1471

Summary

  • Pages Router <Link> and router.push() now preserve query strings in href and navigation URL, matching Next.js. Trailing-slash normalisation preserves the query.

The root cause was parseQueryString (and the parallel parseQuery definition embedded into the Pages Router worker entry) using url.split("?")[1], which drops everything after the second ?. URLs like /linker?href=/about?hello=world have query href=/about?hello=world per RFC 3986, so the previous code truncated query.href to /about and <Link> rendered without the embedded query string.

Test plan

  • Test asserts query string is preserved through Link render and navigation
  • vp check passes

`parseQueryString` and the parseQuery template embedded in the Pages
Router worker entry both used `url.split("?")[1]`, which silently drops
everything after the second `?`. URLs like `/linker?href=/about?hello=world`
have query `href=/about?hello=world` per RFC 3986, so the previous
implementation truncated `query.href` to `/about` and `<Link>` rendered
without the embedded query string. Switching to an `indexOf`-based slice
keeps the full query value intact.

Closes #1471
@james-elicx
Copy link
Copy Markdown
Member Author

/bigbonk review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 22, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1572

commit: 8f3b410

@github-actions
Copy link
Copy Markdown

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
web preview production

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Clean, correct fix. The root cause analysis is accurate — split("?")[1] drops everything after the second ?, violating RFC 3986 where only the first ? delimits path from query.

Both parseQueryString (shared utility) and the inline parseQuery in the generated Pages entry now correctly use indexOf("?") + slice(), and the hash-stripping logic is correct (indexOf("#", queryIndex + 1) searches only within the query portion).

Tests: Good coverage — unit tests for parseQueryString (basic parsing, multi-value, embedded ?, hash stripping) plus integration tests for the SSR <Link> render path with and without trailing slash. The fixture mirrors Next.js's test/e2e/trailing-slashes/pages/linker.js, which is exactly what AGENTS.md asks for.

Dev/prod parity: I checked all server entry points. dev-server.ts imports the shared parseQueryString (already fixed). prod-server.ts delegates query parsing to the built entry's parseQuery (already fixed). App Router entries don't parse query strings into objects — they use the URL constructor. No parity gap.

One remaining instance: packages/vinext/src/index.ts:2801 still has url.split("?")[1] in the image optimization dev passthrough. It's low-risk in practice since <Image> URL-encodes the url parameter, but it's technically susceptible to the same bug and inconsistent with the rest of the codebase. Worth a follow-up.

Ship it.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 22, 2026

Review posted and approved on PR #1572. Here's the summary:

Verdict: Approved. The fix is correct and well-tested.

  • The root cause (split("?")[1] truncating after the second ?) is accurately identified and properly fixed in both the shared parseQueryString utility and the inline parseQuery in the generated Pages entry.
  • Test coverage is solid: unit tests for the parser and integration tests for the SSR <Link> render path, with the fixture ported from Next.js's trailing-slashes test suite.
  • Dev/prod server parity is clean -- all other server entry points either use the shared (now-fixed) utility or don't parse query strings into objects at all.
  • I flagged one remaining split("?")[1] at packages/vinext/src/index.ts:2801 (image optimization dev passthrough) as a low-risk follow-up.

github run

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.

Pages Router: <Link> / router.push() strip query string from href

1 participant