nyphur
nyphur

Reputation: 2896

How do I add a query param to Router.push in NextJS?

With NextJS, I'm trying to route to another page using an object that has a to and as field:

export const routes = {
  'BrowseList' : {
    'to' : '/apps/Browse/list',
    'as' : '/browse/list'
  }
  // ....
}

and then that's imported and used like so:

import { routes } from './__routes';
import Router from 'next/router';

// .... 

const { to, as } = routes.BrowseList;

Router.push(to, as);

which all works. My dilemma is that I'm trying to do something similar to this while attaching a query param. I'm trying to follow this example according to the docs:

Router.push({
  pathname: '/about',
  query: { name: 'Zeit' },
})

What I've tried (which doesn't work):

Router.push({
  pathname : to,
  as,
  query    : { user_id: this.props.data.member.user.id },
});

which gives me a console warning of

Unknown key passed via urlObject into url.format: as

I know I can maybe possibly just use string interpolation and do something like this:

Router.push(to, `${as}?user_id=`${this.props.data.member.user.id}`)

but I was wondering if there was something I'm missing in the doc's example that also adds the query param into my as value.

Thank you.

Upvotes: 27

Views: 72404

Answers (5)

Cris Cirhuza
Cris Cirhuza

Reputation: 30

"use client";
import { usePathname, useRouter, useSearchParams } from "next/navigation";

const Page = () => {
    const router = useRouter();
    const pathname = usePathname();
    const queries = useSearchParams();

    const addQueryParam = () => {
        const params = new URLSearchParams(Array.from(queries.entries()));
        params.set("param", "value");
        router.push(`${pathname}?${params.toString()}`);
    };

    return <button onClick={addQueryParam}>Add query param</button>;
};

export default Page;

Upvotes: 1

Zahit Rios
Zahit Rios

Reputation: 179

For next js >= 13 you can use usePathname hook to get the curren path and with the URLSearchParams class you can set query params and then use toString method to flat them.

"use client";
import { usePathname, useRouter } from "next/navigation";

const Page = () => {
    const router = useRouter();
    const pathname = usePathname();

    const addQueryParam = () => {
        const params = new URLSearchParams();
        params.set("param", "value");
        router.push(`${pathname}?${params.toString()}`);
    };

    return <button onClick={addQueryParam}>Add query param</button>;
};

export default Page;

Upvotes: 3

CTS_AE
CTS_AE

Reputation: 14903

Edit: at first I thought merging an object via spreading would be an easy fix, but then as a comment pointed out there needed to be some changes, so I have updated my answer to still utilize spreading, but unfortunately it does now make the Routes more complicated and involved, but the consumption of it is still straight forward.

I would also freeze the Routes object for peace of mind as well.

import Router from 'next/router';

export const Routes = Object.freeze({
  BrowseList(query) {
    return [
      {
        pathname: '/apps/Browse/list',
        query
      },
      '/browse/list'
    ]
  }
  // ....
})

Router.push(
  ...Routes.BrowseList({
    paramName: "Param value here"
  })
)

Additional Abstraction

import Router from 'next/router';

const QueryRoutePath = (to, as, query) => ([
  {
    pathname: to,
    query
  },
  as
])

export const Routes = Object.freeze({
  BrowseList(query) {
    return QueryRoutePath(
      '/apps/Browse/list',
      '/browse/list',
      query)
  }
  // ....
})

const query = {
  paramName: "Param value here"
}

Router.push(
  ...Routes.BrowseList(query)
)

Upvotes: -1

RCarranza
RCarranza

Reputation: 822

You were close @nyphur. The as value goes as the second parameter of push and not inside the object that corresponds to to (check router.d.ts to see how push is defined). That's why you're getting the error Unknown key passed via urlObject into url.format: as. After 10 months from your question maybe this could still be useful to someone looking for an answer. Assuming you have a way to build the query string for the as parameter, following @Gezim answer or by any other approach:

Router.push({ pathname: to, query: { user_id: this.props.data.member.user.id } }, as, options);

NOTE: Based on @Gezim answer, if you format the string or pathname in the first parameter to contain your query params, it'll work BUT encoded values, if any, like %2B for instance will be decoded so you will get +. This doesn't happen if the query params object go inside query. Consider this if you have any kind of logic that depends on this.

Upvotes: 40

Gezim
Gezim

Reputation: 7328

It appears that the router in next.js doesn't have any convenient API to navigate to using a query string.

I created a utility class called LinkCreator with a toQuery method as follows. It uses query-string to create the query string:

import * as qs from 'query-string';
export class LinkCreator {
    static query(object) {
        return qs.stringify(object);
    }
    static toQuery(object, path = "/") {
        const query = this.query(object);
        return path + '?' + query;
    }
}

Then, it can be used with Router.push like so:

Router.push(LinkCreator.toQuery({ name: 'Zeit' }), '/about');

Upvotes: 1

Related Questions