228

I am trying to set query params with Vue-router when changing input fields, I don't want to navigate to some other page but just want to modify url query params on the same page, I am doing like this:

this.$router.replace({ query: { q1: "q1" } })

But this also refreshes the page and sets the y position to 0, ie scrolls to the top of the page. Is this the correct way to set the URL query params or is there a better way to do it.


Edited:

Here is my router code:

export default new Router({
  mode: 'history',
  scrollBehavior: (to, from, savedPosition)  => {
    if (to.hash) {
      return {selector: to.hash}
    } else {
      return {x: 0, y: 0}
    }
  },
  routes: [
    ....... 
    { path: '/user/:id', component: UserView },
  ]
})
Saurabh
  • 71,488
  • 40
  • 181
  • 244

15 Answers15

270

Here is the example in docs:

// with query, resulting in /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})

Ref: https://router.vuejs.org/en/essentials/navigation.html

As mentioned in those docs, router.replace works like router.push

So, you seem to have it right in your sample code in question. But I think you may need to include either name or path parameter also, so that the router has some route to navigate to. Without a name or path, it does not look very meaningful.

This is my current understanding now:

  • query is optional for router - some additional info for the component to construct the view
  • name or path is mandatory - it decides what component to show in your <router-view>.

That might be the missing thing in your sample code.

EDIT: Additional details after comments

Have you tried using named routes in this case? You have dynamic routes, and it is easier to provide params and query separately:

routes: [
    { name: 'user-view', path: '/user/:id', component: UserView },
    // other routes
]

and then in your methods:

this.$router.replace({ name: "user-view", params: {id:"123"}, query: {q1: "q1"} })

Technically there is no difference between the above and this.$router.replace({path: "/user/123", query:{q1: "q1"}}), but it is easier to supply dynamic params on named routes than composing the route string. But in either cases, query params should be taken into account. In either case, I couldn't find anything wrong with the way query params are handled.

After you are inside the route, you can fetch your dynamic params as this.$route.params.id and your query params as this.$route.query.q1.

Mani
  • 23,635
  • 6
  • 67
  • 54
  • 1
    I tried giving the path also, but this did not stop scrolling to the top of the page, I have edited the question with router options also, may be there is some change needed there. – Saurabh Nov 03 '16 at 06:10
  • Is your query param intended to scroll to the right place in the document? Like your other question on [anchor tags](http://stackoverflow.com/questions/40341939/how-to-create-anchor-tags-with-vue-router)? – Mani Nov 03 '16 at 06:57
  • 1
    No, I just want to add the query param in URL, I don't want any scroll here. – Saurabh Nov 03 '16 at 07:31
  • I just tested the options in my local setup, the query params work normally. I am able to navigate to new route and access query params as shown in my updated answer. So, the problem is - you do not want it to scroll? Or the problem is the entire app refreshing again? – Mani Nov 03 '16 at 09:05
  • 1
    so I am on the same page, when I select some input, I want to add them in the URL, but when I do it, scroll happens. Scroll is the issue for me. I am not trying to navigate to other page, I just want to be on same page and add/modify url query params seemlessly. – Saurabh Nov 03 '16 at 10:56
  • Here is an [old discussion](https://github.com/vuejs/vue-router/issues/463) on this topic. The only way to change query params is to use `$router.push` or `$router.replace`, which navigates to the same route again. Now the component is refreshed and it scrolls back to top. If you are trying to do infinite-scroll or equivalent, your only option is to identify the query params, process it and scroll to the relevant section in the bottom somehow. Based on my current knowledge, I don't think you can change query params while remaining in the same route. Maybe we can wait for a different answer. – Mani Nov 03 '16 at 11:36
  • it does not work for me. goToConnect() { this.$router.push({name: 'Dash.Client.Connect', query:{q1: 'q1'}, params: {email: 'abc'} }) } – Nicolas S.Xu Oct 03 '17 at 00:29
  • how can I set the query params in `hash` mode – masongzhi Sep 21 '19 at 08:01
  • @masongzhi If you do not set `history` mode, vue router will automatically stay in its `hash` mode default. You can view the [config reference here](https://router.vuejs.org/guide/essentials/history-mode.html). To get `hash` mode, skip any reference to `mode` in vue-router config. – Mani Sep 25 '19 at 06:29
  • is it possible to do this in the template with a tag? adding a query param, i know i can set the "to" attribute to the route name but not sure about "query" param. may be a separate question i didn't see an SO question about this – Akin Hwan Nov 26 '20 at 22:15
  • this doesn't work with nuxt. when you provide name param, and your route is for example categories/:id, then it will replace route with /categories, ignoring the :id, even if you provide proper path and query. And without name param, it will simply fail to do anything at all ( – tylik Oct 03 '21 at 20:27
36

Without reloading the page or refreshing the dom, history.pushState can do the job.
Add this method in your component or elsewhere to do that:

addParamsToLocation(params) {
  history.pushState(
    {},
    null,
    this.$route.path +
      '?' +
      Object.keys(params)
        .map(key => {
          return (
            encodeURIComponent(key) + '=' + encodeURIComponent(params[key])
          )
        })
        .join('&')
  )
}

So anywhere in your component, call addParamsToLocation({foo: 'bar'}) to push the current location with query params in the window.history stack.

To add query params to current location without pushing a new history entry, use history.replaceState instead.

Tested with Vue 2.6.10 and Nuxt 2.8.1.

Be careful with this method!
Vue Router don't know that url has changed, so it doesn't reflect url after pushState.

doppelgreener
  • 4,809
  • 10
  • 46
  • 63
ManUtopiK
  • 4,495
  • 3
  • 38
  • 52
28

Actually you can push query like this: this.$router.push({query: {plan: 'private'}})

Based on: https://github.com/vuejs/vue-router/issues/1631

kissu
  • 40,416
  • 14
  • 65
  • 133
jean d'arme
  • 4,033
  • 6
  • 35
  • 70
  • 44
    "But this also refreshes the page" – digout Mar 07 '19 at 14:49
  • 1
    Not sure about Vue2 but works like a charm in Vue3 (without page-refresh) – Arno van Oordt Dec 30 '21 at 10:46
  • @ArnovanOordt It also reloads the page in Vue 3. – kleinfreund Sep 07 '22 at 07:55
  • 2
    There is a misunderstanding here about what "refreshes the page" means here. A hard refresh (like F5) and an actual vue-router client-side navigation are 2 different things. Here, jean's solution does not trigger a "page hard refresh", meanwhile it will trigger a vue-router navigation and possible mount/unmount specific components. Things that OP do not want indeed. – kissu Dec 03 '22 at 17:28
27

Okay so i've been trying to add a param to my existing url wich already have params for a week now lol, original url: http://localhost:3000/somelink?param1=test1 i've been trying with:

this.$router.push({path: this.$route.path, query: {param2: test2} });

this code would juste remove param1 and becomes http://localhost:3000/somelink?param2=test2

to solve this issue i used fullPath

this.$router.push({path: this.$route.fullPath, query: {param2: test2} });

now i successfully added params over old params nd the result is

http://localhost:3000/somelink?param1=test1&param2=test2

nab
  • 568
  • 8
  • 20
19

If you are trying to keep some parameters, while changing others, be sure to copy the state of the vue router query and not reuse it.

This works, since you are making an unreferenced copy:

  const query = Object.assign({}, this.$route.query);
  query.page = page;
  query.limit = rowsPerPage;
  await this.$router.push({ query });

while below will lead to Vue Router thinking you are reusing the same query and lead to the NavigationDuplicated error:

  const query = this.$route.query;
  query.page = page;
  query.limit = rowsPerPage;
  await this.$router.push({ query });

Of course, you could decompose the query object, such as follows, but you'll need to be aware of all the query parameters to your page, otherwise you risk losing them in the resultant navigation.

  const { page, limit, ...otherParams } = this.$route.query;
  await this.$router.push(Object.assign({
    page: page,
    limit: rowsPerPage
  }, otherParams));
);

Note, while the above example is for push(), this works with replace() too.

Tested with vue-router 3.1.6.

Andre M
  • 6,649
  • 7
  • 52
  • 93
14

Here's my simple solution to update the query params in the URL without refreshing the page. Make sure it works for your use case.

const query = { ...this.$route.query, someParam: 'some-value' };
this.$router.replace({ query });
parker_codes
  • 3,267
  • 1
  • 19
  • 27
  • How do you access this.$route.query in vue3 composition API ? – ii iml0sto1 Jun 14 '22 at 13:16
  • @iiiml0sto1 for accessing the root element in Composition API i use `import { getCurrentInstance } from 'vue'` on top and in the code `const root = getCurrentInstance();` and `var query = root.proxy.$route.query` – stif Oct 13 '22 at 10:09
  • @iiiml0sto1 check that one: https://stackoverflow.com/a/67357142/8816585 – kissu Dec 03 '22 at 17:30
  • I would recomend `import { useRoute } from 'vue-router';` and then `const route = useRoute();` and `console.log('query', route.query);` – agm1984 Feb 13 '23 at 19:00
9

My solution, no refreshing the page and no error Avoided redundant navigation to current location

    this.$router.replace(
      {
        query: Object.assign({ ...this.$route.query }, { newParam: 'value' }),
      },
      () => {}
    )
hayumi kuran
  • 391
  • 9
  • 21
7

You could also just use the browser window.history.replaceState API. It doesn't remount any components and doesn't cause redundant navigation.

window.history.replaceState(null, '', '?query=myquery');

More info here.

Ludolfyn
  • 1,806
  • 14
  • 20
  • 1
    second argument should be a string, so `window.history.replaceState(null, '', '?query=myquery');` – commonpike May 28 '22 at 12:36
  • The `Vue-router` will throw a history state missing warning. So instead of null, pass the `history.state` like: `window.history.replaceState(history.state, '', '?query=myquery');` – Shamim Hossain Jan 23 '23 at 21:11
6
this.$router.push({ query: Object.assign(this.$route.query, { new: 'param' }) })
double-beep
  • 5,031
  • 17
  • 33
  • 41
Boston Kenne
  • 778
  • 10
  • 15
  • 2
    I liked this answer the best. Unfortunately this causes `Error: Avoided redundant navigation to current location` – Max Coplan Jun 25 '20 at 16:49
  • 1
    Fix: `this.$router.push({ query: Object.assign({...this.$route.query}, { new: 'param' }) })` – Max Coplan Jun 25 '20 at 16:59
  • 3
    But now that I think about it you can just do `this.$router.push({ query: {...this.$route.query,new: 'param'},) })` – Max Coplan Jun 25 '20 at 17:01
  • How do you access this.$route.query in vue3 composition API ? – ii iml0sto1 Jun 14 '22 at 13:15
  • 1
    @iiiml0sto1 Something like: ```js import { useRouter, useRoute } from 'vue-router'; const router = useRouter(); const route = useRoute(); // then in your logic const query = Object.assign(route.value.query, { new: 'param' }); router.value.push({ query }); ``` – parker_codes Jun 17 '22 at 22:28
  • @iiiml0sto1 check that one: https://stackoverflow.com/a/67357142/8816585 – kissu Dec 03 '22 at 17:30
5

For adding multiple query params, this is what worked for me (from here https://forum.vuejs.org/t/vue-router-programmatically-append-to-querystring/3655/5).

an answer above was close … though with Object.assign it will mutate this.$route.query which is not what you want to do … make sure the first argument is {} when doing Object.assign

this.$router.push({ query: Object.assign({}, this.$route.query, { newKey: 'newValue' }) });
Boris
  • 105
  • 1
  • 5
3

To set/remove multiple query params at once I've ended up with the methods below as part of my global mixins (this points to vue component):

    setQuery(query){
        let obj = Object.assign({}, this.$route.query);

        Object.keys(query).forEach(key => {
            let value = query[key];
            if(value){
                obj[key] = value
            } else {
                delete obj[key]
            }
        })
        this.$router.replace({
            ...this.$router.currentRoute,
            query: obj
        })
    },

    removeQuery(queryNameArray){
        let obj = {}
        queryNameArray.forEach(key => {
            obj[key] = null
        })
        this.setQuery(obj)
    },
vir us
  • 9,920
  • 6
  • 57
  • 66
3

I normally use the history object for this. It also does not reload the page.

Example:

history.pushState({}, '', 
                `/pagepath/path?query=${this.myQueryParam}`);
mostafa
  • 324
  • 2
  • 8
3

This is the equivalent using the Composition API

<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()

router.push({ path: 'register', query: { plan: 'private' }})
</script>

You can also use the Vue devtools just to be sure that it's working as expected (by inspecting the given route you're on) as shown here: https://stackoverflow.com/a/74136917/8816585


Update

That will meanwhile mount/unmount components. Some vanilla JS solution is still the best way to go for that purpose.

kissu
  • 40,416
  • 14
  • 65
  • 133
2

The vue router keeps reloading the page on update, the best solution is

  const url = new URL(window.location);
  url.searchParams.set('q', 'q');
  window.history.pushState({}, '', url);
        
Sachin S
  • 186
  • 8
1

With RouterLink

//With RouterLink
<router-link 
  :to="{name:"router-name", prams:{paramName: paramValue}}"
>
Route Text
</router-link>

//With Methods

methods(){
  this.$router.push({name:'route-name', params:{paramName: paramValue}})
}

With Methods

methods(){
  this.$router.push({name:'route-name', params:{paramName, paramValue}})
}
Zakirsoft
  • 21
  • 3
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 19 '22 at 11:42