Changing Scroll Behavior with Vue Router
Tags: webdev, programming, javascript, vue
Published: 2021-01-25 23:24:25

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

Vue Router is a URL router that maps URLs to components.

In this article, we’ll look at how to change the scroll behavior of Vue Router routes.

Changing Scroll Behavior
========================

Sometimes we may want to scroll to top when navigating to a new route or preserve the scrolling position of history entities like a real page reload.

Vue Router lets us adjust the scrolling behavior the way we like when route loads.

This only works if the browser supports `history.pushState` .

We can adjust scrolling behavior by providing a `scrollBehavior` function.

It has a `to` and `from` route objects as the first 2 parameters. The third argument is `savePosition` , is only available if this is a `popstate` navigation triggered by the browser’s back or forward buttons.

The function can return a scroll position object in the form of:

```
{ x: number, y: number }
```

or:

```
{ selector: string, offset? : { x: number, y: number }}
```

`offset` is supported since Vue Router 2.6.0

For example, we can add `scrollBehavior` to our routes as follows:

`src/index.js:`

```
const Foo = {  
  template: `  
    <div>  
      <div v-for='n in 100'>{{n}} foo</div>  
    </div>  
  `  
};  
const Bar = {  
  template: `  
  <div>  
    <div v-for='n in 100'>{{n}} bar</div>  
  </div>  
  `  
};

const routes = [  
  {  
    path: "/foo",  
    component: Foo  
  },  
  {  
    path: "/bar",  
    component: Bar  
  }  
];

const router = new VueRouter({  
  routes,  
  scrollBehavior(to, from, savedPosition) {  
    return { x: 0, y: 0 };  
  }  
});new Vue({  
  el: "#app",  
  router  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
      <router-link to="foo">Foo</router-link>  
      <router-link to="bar">Bar</router-link>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>
```

In the code above, we have:

```
scrollBehavior(to, from, savedPosition) {  
  return { x: 0, y: 0 };  
}
```

to scroll the next page back to the top when we navigate. Therefore, when we click on the links at the bottom of the page, we’ll go back to the top.

If we return something falsy or an empty object, no scrolling will happen.

If we change `scrollBehavior` to:

```
scrollBehavior(to, from, savedPosition) {  
  if (savedPosition) {  
    return savedPosition;  
  } else {  
    return { x: 0, y: 0 };  
  }  
}
```

Then we keep the scroll position of the page when we navigate back and forth with the forward and back buttons.

We can create the scroll to anchor behavior by setting the ID on our elements and then changing `scrollBehavior` as follows:

`src/index.js` :

```
const Foo = {  
  template: `  
    <div>  
      <div v-for='n in 100' :id='n'>{{n}} foo</div>  
    </div>  
  `  
};

const routes = [  
  {  
    path: "/foo",  
    component: Foo  
  }  
];

const router = new VueRouter({  
  routes,  
  scrollBehavior(to, from, savedPosition) {  
    if (to.hash) {  
      return {  
        selector: to.hash  
      };  
    }  
  }  
});

new Vue({  
  el: "#app",  
  router  
});
```

`index.html` :

```
<!DOCTYPE html>  
<html>  
  <head>  
    <title>App</title>  
    <meta charset="UTF-8" />  
    <script src="https://unpkg.com/vue/dist/vue.js"></script>  
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>  
  </head>  
  <body>  
    <div id="app">  
      <router-view></router-view>  
    </div>  
    <script src="src/index.js"></script>  
  </body>  
</html>
```

Then when we go to `/#/foo#100` we scroll to the bottom, then when we go to `/#/foo#20` , we see `20 foo` at the top of the screen.

Async Scrolling
===============

We can also to async scrolling with `scrollBehavior` since Vue Router 2.8.0 by returning a promise that resolves to the same objects as synchronous scrolling.

For example, we can change the example above to:

`src/index.js` :

```
const Foo = {  
  template: `  
    <div>  
      <div v-for='n in 100' :id='n'>{{n}} foo</div>  
    </div>  
  `  
};

const routes = [  
  {  
    path: "/foo",  
    component: Foo  
  }  
];

const router = new VueRouter({  
  routes,  
  scrollBehavior(to, from, savedPosition) {  
    if (to.hash) {  
      return new Promise((resolve, reject) => {  
        setTimeout(() => {  
          resolve({  
            selector: to.hash  
          });  
        }, 1000);  
      });  
    }  
  }  
});

new Vue({  
  el: "#app",  
  router  
});
```

Then when we go to `/#/foo#100` in our browser, we scroll to the bottom of the page after a second.

Conclusion
==========

Scrolling behavior during route navigation can be changed by adding a `scrollBehavior` function to our `router` object. It takes the `to` and `from` parameters which are route objects, and a 3rd `savedPosition` parameter with the saved scroll position of the navigated route.

Scrolling behavior can be synchronous or asynchronous since Vue Router 2.8.0.

We can scroll to an element with the CSS selector or position.