'NextJS - Appending a query param to a dynamic route

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



Solution 1:[1]

Just add more param to current router then push itself

const router = useRouter();
router.query.NEWPARAMS = "VALUE"
router.push(router)

Solution 2:[2]

If we want to have this as a link - use it like so:

// ...
const { query } = useRouter();
// ...

<Link
    href={{
        pathname: router.pathname,
        query: { ...query, lang },
    }}
    passHref
    shallow
    replace
></Link>

Solution 3:[3]

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);
  };

Solution 4:[4]

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 },
});

Solution 5:[5]

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: enter image description here

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.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Cong Nguyen
Solution 2
Solution 3 Jean-Francois Gagnon
Solution 4 Maciej Sikora
Solution 5 albert