Skip to content

feat: allow resolve() to accept pathnames with query strings and hash fragments#15458

Merged
teemingc merged 4 commits intosveltejs:mainfrom
BridgerB:feat/resolve-accept-query-hash-types
Mar 3, 2026
Merged

feat: allow resolve() to accept pathnames with query strings and hash fragments#15458
teemingc merged 4 commits intosveltejs:mainfrom
BridgerB:feat/resolve-accept-query-hash-types

Conversation

@BridgerB
Copy link
Contributor

Summary

Adds a ResolvablePath type that extends Pathname with optional ?${string} and #${string} suffixes, allowing resolve() to accept pathnames with query strings and hash fragments.

  • No runtime changes — resolve_route() already passes through ? and # correctly
  • Pathname type is unchanged (still used for page.url.pathname)
  • Existing resolve() behavior with route IDs and params is unaffected

Motivation

Currently there is no way to use goto() with query strings or hash fragments without either disabling the lint rule or bypassing resolve():

  1. resolve('/products?page=2') fails type-checking (Pathname doesn't include ?)
  2. goto(resolve('/products') + '?page=2') fails the svelte/no-navigation-without-resolve lint rule (argument isn't a resolve() call)

Per @teemingc's suggestion in #14750:

"It should be possible to add a union type with a template string such as /my-route#${string} and /my-route?${string}. This would continue to keep the function type-safe."

This is a minimal type-only fix that doesn't conflict with #14756 (which adds richer { hash, searchParams } options).

What this enables

// All of these now type-check AND satisfy the lint rule:
goto(resolve('/products?page=2'));
goto(resolve(`/search?${params}`));
goto(resolve('/docs/intro#getting-started'));

Test plan

  • pnpm check passes
  • pnpm lint passes
  • Existing tests pass (no runtime changes, only type widening)

Closes #14750

… fragments

- Add `ResolvablePath` type: `Pathname | ${Pathname}?${string} | ${Pathname}#${string}`
- Update `resolve()` and `resolveRoute()` signatures to accept `ResolvablePath`
- No runtime changes needed — `resolve_route()` already passes through `?` and `#`
- Enables `goto(resolve('/products?page=2'))` to satisfy both type checker and `svelte/no-navigation-without-resolve` lint rule

Closes sveltejs#14750
@changeset-bot
Copy link

changeset-bot bot commented Feb 28, 2026

🦋 Changeset detected

Latest commit: 977191d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@svelte-docs-bot
Copy link

Copy link
Contributor

@vercel vercel bot left a comment

Choose a reason for hiding this comment

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

Additional Suggestion:

The resolve() function signature uses Pathname instead of ResolvablePath, preventing the use of query strings and hash fragments.

Fix on Vercel

- Update resolve() in types/index.d.ts (was still using Pathname)
- Update JSDoc template in client.js
Copy link
Member

@teemingc teemingc left a comment

Choose a reason for hiding this comment

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

Thank you for submitting this. I've added a few comments to help align things with our current conventions. Other than that, it looks good to me.

@teemingc teemingc linked an issue Mar 2, 2026 that may be closed by this pull request
Copy link
Contributor

Choose a reason for hiding this comment

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

I love this, but it's missing one crucial bit. Because this only applies to pathnames, it means you can't do this:

resolve('/[slug]?foo=bar', { slug: 'banana' });

...because route parameter notation like this falls under the RouteId type. I think I'd prefer if the ?{string} and #{string} variants applied to the RouteId version as well, though... that might be challenging. want to give it a crack?

@teemingc
Copy link
Member

teemingc commented Mar 2, 2026

I love this, but it's missing one crucial bit. Because this only applies to pathnames, it means you can't do this:

resolve('/[slug]?foo=bar', { slug: 'banana' });

...because route parameter notation like this falls under the RouteId type. I think I'd prefer if the ?{string} and #{string} variants applied to the RouteId version as well, though... that might be challenging. want to give it a crack?

Would that also be something like:

export type RouteIdWithSearchOrHash = RouteId |
	`${RouteId}?${string}` |
	`${RouteId}#${string}`

@elliott-with-the-longest-name-on-github
Copy link
Contributor

Yeah I think so, but I'm not sure how it would affect our ability to extract the types of the route parameters for the second argument...

- Rename ResolvablePath to PathnameWithSearchOrHash
- Remove redundant ${Pathname}?${string}#${string} variant
- Add RouteIdWithSearchOrHash type so resolve('/[slug]?foo=bar', { slug: 'banana' }) works
- Update ResolveArgs to extract params from RouteId with search/hash suffix
- Shorten changeset text
@teemingc
Copy link
Member

teemingc commented Mar 3, 2026

Yeah I think so, but I'm not sure how it would affect our ability to extract the types of the route parameters for the second argument...

The contributed solution really is quite brilliant 😄 https://github.com/sveltejs/kit/pull/15458/changes#diff-ca363a5801bb1240cce13fcdf36af610cfa8d5aae2007e77244a0d91f9ce5b4dR8-R12 I've tested it along with params that have matchers and it works well!

@teemingc teemingc merged commit 5c79eb4 into sveltejs:main Mar 3, 2026
23 of 26 checks passed
@github-actions github-actions bot mentioned this pull request Mar 3, 2026
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.

Add optional hash and searchParams arguments to resolve() Typescript error when resolve has a query string parameter

3 participants