Reputation: 3290
In my NextJS app, I have a language selector that's visible on every page. When I select a new language, I just want to replace the current URL by appending a query param lang=en
to it.
Here's the function that replaces the URL:
const changeLanguage = (lang: LanguageID) => {
replace({
pathname,
query: { ...query, lang },
});
};
In this example, replace
, query
and pathname
are coming from the next router.
Now, everything works for static routes, but I'm unable to make it work for dynamic routes. For example, I have the following folder structure:
pages
|_customers
|__index.tsx
|__[customerId].tsx
If I'm on http://localhost/customers
and I change my language to English, the URL changes to http://localhost/customers?lang=en
which is what I want. However, if I'm on http://localhost/customer/1
and I change my language to English, the URL changes to http://localhost/customers/[customerId]?customerId=1&lang=en
, instead of the URL I'm expecting http://localhost/customers/1?lang=en
.
Now, I know that I could use asPath
on the router, and reconstruct the query string object by appending lang
to it, but I feel that it's something that should be build into Next. Also, I know it could be easily done with vanilla JS, but it's not the point here.
Am I missing something? Is there an easier way to append query params to a dynamic route without doing a server-side re-rendering?
Thanks
Upvotes: 60
Views: 164617
Reputation: 9426
If we want to have this as a link - use it like so:
const router = useRouter();
<Link
href={{
pathname: router.pathname,
query: { ...router.query, lang },
}}
passHref
shallow
replace
></Link>
Upvotes: 22
Reputation: 47
I see the top answer already resolved this, just wanted to add, if you have for example table filters or page result filters, do not do ‘router.replace()’. Always push to preserve the browser history. This way the user can go back with browser arrows and query params will still work.
Upvotes: 0
Reputation: 107
If you are using App Router, The query
object has been removed and is replaced by useSearchParams()
, you need to update searchParams
:
export default function ExampleClientComponent() {
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
// Get a new searchParams string by merging the current
// searchParams with a provided key/value pair
const createQueryString = useCallback(
(name: string, value: string) => {
const params = new URLSearchParams(searchParams.toString())
params.set(name, value)
return params.toString()
},
[searchParams]
)
return (
<>
{/* using useRouter */}
<button
onClick={() => {
// <pathname>?sort=asc
router.push(pathname + '?' + createQueryString('sort', 'asc'))
}}
>
ASC
</button>
{/* using <Link> */}
<Link
href={
// <pathname>?sort=desc
pathname + '?' + createQueryString('sort', 'desc')
}
>
DESC
</Link>
</>
)
}
Upvotes: 2
Reputation: 83
for nextjs 13 I just found this,
import { useRouter } from 'next/navigation';
router.push(`?page=${page}`)
you just need to handle every query you want
Upvotes: 4
Reputation: 1
The best solution I could come up with is that doesn't cause the Unknown key passed via urlObject into url.format
is to do this pattern router.query.plan = plan.title; router.push({ query: router.query });
Upvotes: 0
Reputation: 469
In latest version, Next 13, some of the functionality moved to other hooks, which query and path are some of them. You can use useSearchParams
to get query and usePathname
instead of pathname. By the time I am writing this answer, it does not have a stable version and you can find the beta documents here:
https://beta.nextjs.org/docs/api-reference/use-router
Upvotes: 2
Reputation: 49351
let queryParams;
if (typeof window !== "undefined") {
// The search property returns the querystring part of a URL, including the question mark (?).
queryParams = new URLSearchParams(window.location.search);
// quaeryParams object has nice methods
// console.log("window.location.search", queryParams);
// console.log("query get", queryParams.get("location"));
}
inside changeLanguage
,
const changeLanguage = (lang: LanguageID) => {
if (queryParams.has("lang")) {
queryParams.set("lang", lang);
} else {
// otherwise append
queryParams.append("lang", lang);
}
router.replace({
search: queryParams.toString(),
});
};
Upvotes: -1
Reputation: 845
An alternative approach when you have dynamic routing in Next.js, and want to do a shallow adjustment of the route to reflect updated query params, is to try:
const router = useRouter()
const url = {
pathname: router.pathname,
query: { ...router.query, page: 2 }
}
router.push(url, undefined, { shallow: true })
This will retreive the current path (router.pathname
) and query (router.query
) details, and merge them in along with your new page
query param. If your forget to merge in the existing query params you might see an error like:
The provided href value is missing query values to be interpolated properly
Upvotes: 10
Reputation: 99
If anyone is still looking the answer ,for Next,js ^11.1.2.I hope this helps you out.Use
const router = useRouter();
router.push({ pathname: "/search", query: { key: key } });
Upvotes: 2
Reputation: 20152
The solution which doesn't need to send the whole previous route, as replace
just replaces what we need to replace, so query params:
const router = useRouter();
router.replace({
query: { ...router.query, key: value },
});
Upvotes: 36
Reputation: 192
I tried adding my param to the route query and pushing the router itself, as mentioned here, it works, but I got a lot of warnings:
So, I then pushed to /
and passed my query params as following:
const router = useRouter();
router.push({ href: '/', query: { myQueryParam: value } });
I hope that works for you too.
Upvotes: 10
Reputation: 3445
Just add more param to current router then push itself
const router = useRouter();
router.query.NEWPARAMS = "VALUE"
router.push(router)
Upvotes: 72
Reputation: 3290
I ended up using the solution that I wanted to avoid in the first place, which was to play with the asPath
value. Atleast, there's no server-side re-rendering being done since the path is the same.
Here's my updated changeLanguage
function (stringifyUrl
is coming from the query-string
package)
const changeLanguage = (lang: LanguageID) => {
const newPathname = stringifyUrl({ url: pathname, query: { ...query, lang } });
const newAsPath = stringifyUrl({ url: asPath, query: { lang } });
replace(newPathname, newAsPath);
};
Upvotes: 2