Replies: 2 comments 1 reply
-
This is an excellent feature request that addresses a real inconsistency between the Current Workarounds1. Using Object-style navigation (similar to Link)You can already use an object-style approach with import { useRouter } from 'next/navigation'
const router = useRouter()
// This works for basic cases
const navigateWithQuery = () => {
// You still need to manually construct the URL
const searchParams = new URLSearchParams(window.location.search)
searchParams.set('newQuery', 'true')
router.push(`/new-path?${searchParams.toString()}`)
} 2. Helper function approachYou can create a utility function to bridge this gap: import { useRouter } from 'next/navigation'
interface NavigationOptions {
pathname: string
search?: string
searchParams?: URLSearchParams
hash?: string
scroll?: boolean
}
const useEnhancedRouter = () => {
const router = useRouter()
const pushWithObject = (options: NavigationOptions) => {
let url = options.pathname
if (options.searchParams) {
const params = options.searchParams.toString()
if (params) url += `?${params}`
} else if (options.search) {
url += options.search.startsWith('?') ? options.search : `?${options.search}`
}
if (options.hash) {
url += options.hash.startsWith('#') ? options.hash : `#${options.hash}`
}
router.push(url, { scroll: options.scroll ?? true })
}
return { ...router, pushWithObject }
} Why This Feature Makes SenseYour proposal aligns perfectly with web standards and developer expectations:
Comparison with Current APIsLink component (works today): <Link href={{
pathname: '/dashboard',
query: { tab: 'analytics' }
}}>
Dashboard
</Link> Your proposed useRouter enhancement: router.push({
pathname: '/dashboard',
search: '?tab=analytics',
hash: '#section'
}) Implementation ConsiderationsBased on the official useRouter documentation, your proposal would need to:
Migration BenefitsThis would make migrating from Pages Router much smoother, as developers expect similar object-based navigation APIs across React routing libraries. Current Pages Router style: router.push({
pathname: '/dashboard',
query: { tab: 'analytics' }
}) Your proposed App Router enhancement: router.push({
pathname: '/dashboard',
searchParams: new URLSearchParams({ tab: 'analytics' })
}) This is a well-thought-out proposal that would significantly improve the developer experience. The inconsistency between |
Beta Was this translation helpful? Give feedback.
-
Interesting suggestion. I think, it is worth also exploring what you could do, as a dev in this situation, independently of what the framework decides to do. I remember in Swift, building URLs was kind of easy and removed some of the rough edges, I think JavaScript URL constructor can also do that, to some extend const toPath = (path, params = {}, hash = "") => {
if (!path.startsWith("/")) path = `/${path}`
const noop = "http://noop";
const url = new URL(path, noop);
const searchParams = new URLSearchParams(url.search); // in case the path had queries already
for (const [key, value] of Object.entries(params)) {
searchParams.set(key, value);
}
url.search = searchParams.toString();
url.hash = hash.replace(/^#/, "");
return url.toString().replace(noop, "");
};
toPath("/foo-bar?q=how", {page: 10}, "#how-to") // '/foo-bar?q=how&page=10#how-to' I feel like this reduces quite a bit of the string manipulation otherwise needed. You'd still need to watch out for path shapes, and new URL possibly throwing (can it throw in this case?), perhaps query params collisions in some cases - but generally it looks good |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Goals
next/link
anduseRouter
fromnext/navigation
Non-Goals
Background
I've found it quite confusing that the
Link
component fromnext/link
accepts pretty much all of the parameters of theURL
class, but the programmatic functions fromuseRouter
does not. In the pages router this was supported, and in other frameworks/libraries such as React Router it also is.Right now string manipulation has to be done in user-land, which is quite error prone. Also to my understanding often it's preferable to use
window.location.search
if programmatic navigations in callbacks, to avoid havinguseSearchParams
opting out of static rendering.Here's some examples:
Example 1: Forwarding current search params
This example is not that bad, but it's in my experience quite error prone doing string manipulations like this. Also any issues would not be immediately noticeable unless you actively trigger the programmatic navigation.
Example 2: Setting search params and hash
Here it gets a little worse in my opinion... I believe a lot of devs don't know top of mind if the hash or search string should come first.
Example 3: With
URLSearchParams
Proposal
I suggest that the programmatic navigation functions should support the
API
class, or at least a subset of it. Alternatively it could support theUrlObject
which is what theLink
component does, but the downside with that is that it doesn't seem to support passingURLSearchParams
.With this suggestion the code examples from earlier could instead look something like this:
Example 1: Forwarding current search params
Example 2: Setting search params and hash
Example 3: With
URLSearchParams
I'm definitely interested in contributing! So something along the lines of what I've proposed sounds good, I can (attempt) making a PR for it 😄
Beta Was this translation helpful? Give feedback.
All reactions