'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.



Solution 1:[1]

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

Solution 2:[2]

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

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 Gezim
Solution 2