Skip to content

Add optional hash and searchParams arguments to resolve() #14750

@codevogel

Description

@codevogel

Describe the problem

For internal navigation, I tend to use goto().

The lint rule svelte/no-navigation-without-resolve triggers when calling goto() without wrapping the path in resolve().

However, since resolve() currently only accepts plain /route or /route/[slug] URLs, it's not possible to use goto() with URLs that include a hash or search parameters. Or at least, not without triggering that linting rule.

e.g. this throw the linting rule for both the a tag and the button

<script lang="ts">
	import { goto } from "$app/navigation";
	import { resolve } from "$app/paths";

	const myURL = resolve('/my-page') + '#some-hash';
</script>

<a href={myURL}>My A Tag</a>
<button onclick={() => { goto(myURL) }}>My Button</button>

If we try to apply the tag inside the resolve() function:

<script lang="ts">
	import { goto } from "$app/navigation";
	import { resolve } from "$app/paths";
</script>

<a href={resolve('/my-page#some-hash')}>My A Tag</a>
<button onclick={() => { goto(resolve('/my-page#some-hash')) }}>My Button</button>

We get a type mismatch, which is pretty much the point of the resolve() function:

error| Argument of type '["/my-page#some-hash"]' is not assignable to parameter of type '[route: "/"] | [route: "/my-page"] | [route: "/my-page/"]'. Type '["/my-page#some-hash"]' is not assignable to type '[route: "/my-page/"]'. Type '"/my-page#some-hash"' is not assignable to type '"/my-page/"'.

I've tried several workarounds, including string interpolation and concatenation, but these approaches also trigger the lint rule.

Image

ESLint playground demo

Note: The playground doesn’t actually run SvelteKit, so resolve() doesn’t throw typing errors there as it would locally. There's no /routes/my-page/+page.svelte, but <a href={resolve(hashedSvelteURL)}>my svelte link</a> is still accepted as “valid”.

Currently, the only way to navigate to unresolved or partially resolved paths is via window.location.href, which makes sense since it's intended for external URLs.

However, since linking to fragments like /projects#project-slug is common (and even used throughout the Svelte docs), it seems odd that resolve() doesn’t support searchParams or hash values.

So unless I’m missing something, the only two options at the moment are:

  • ignore the lint rule, or
  • use window.location.href for internal navigation.

Alternatives considered

I searched for documentation or existing discussions on how to handle this within the current linting constraints but couldn’t find any.

If a workaround already exists, perhaps the resolve documentation could be updated to include guidance or examples.


Would you like me to make it sound slightly more “formal” (to match typical framework issue tone), or keep this slightly conversational GitHub style?

Describe the proposed solution

resolve() already supports a slug parameter.

It would be helpful if it also accepted optional arguments like:

resolve(path, { hash?: string, searchParams?: Record<string, string> })

These additions wouldn’t break existing functionality and would make internal navigation with anchors or query parameters lint-compliant.

Implementation-wise, resolve() could run its existing logic first and then append the hash and/or serialized searchParams', similar to how SvelteURL` appends these.

Alternatives considered

I searched for documentation or existing discussions on how to handle this within the current linting constraints but couldn’t find any.

If a workaround already exists, perhaps the resolve documentation could be updated to include a pointer to this workaround.

Alternative solutions would be:

  • Relax the linting rule to allow strings that append hashes or search parameters. However, this feels undesirable, since it’s much cleaner to define hash and searchParams programmatically—similar to how SvelteURL handles them—rather than relying on string interpolation.

  • Add built-in resolve() support for SvelteURL, so that we could call something like goto(mySvelteURL.resolve()).

  • Allow resolve() to take in SvelteURL, and resolve it from there. This might even be the 'prettiest' option. This would probably need extra work so SvelteURL can be expanded with a .slug attribute.

Importance

would make my life easier

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions