Demystifying page transitions with Nuxt


In this post, I’ll show you the basics of working with Nuxt.js, as well as how to create awesome transitions when building apps with Nuxt.


In this post, I’ll show you the basics of working with Nuxt.js, as well as how to create awesome transitions when building apps with Nuxt.

Interrupt a user’s experience with your app and watch them leave. One reason for this can be narrowed down to awkward navigation in your app. When moving from one section to another leaves users figuring out if the app is broken or a page is taking too long to load, improvements need to be made.

Besides being nice to watch and having the ability to boost branding when done well, transitions do a great job to enhance the user experience by preserving the user’s context, keeping their attention on the app as well as providing visual continuity.

Setting up a Nuxt.js Project

So you might ask: “But why Nuxt?” Simply put, Nuxt is a framework used for creating universal Vue.js applications. Inspired by Next.js, a framework for server-rendered React applications, its main scope is UI rendering while abstracting away the client/server distribution. Besides seamlessly adding transitions to your page, Nuxt comes along with amazing features such as:

  • Server-side rendering
  • Extensive support of pre-processors like Sass, Less, and Stylus
  • Easy routing

The Nuxt team has created a starter template to help us get up and running. To set up a Nuxt project, install the starter template via vue-cli:

1vue init nuxt-community/starter-template <project-name>
2    cd <project-name>
4    #install dependencies
5    npm install
7    #launch the project
8    npm run dev

N.B: If vue-cli is not installed, you can install it with npm install -g vue-cli. You just need to do this once.

For the demo, I’m going to name my project nuxt-lite. Feel free to be creative here.

The application will run on http://localhost:3000/. You should see the following welcome page in your browser:

The directory structure of your newly created Nuxt project should look like this:

Back to the browser, let’s find out what’s behind the initial view of our application. In the project folder, navigate to the pages directory and open the index.vue file, inside the file, is all the markup it took to create our application’s landing page. What makes this cool is that our .vue the file doesn’t require any special setup. Once it is placed inside the pages directory Nuxt.JS automatically makes it a server-side rendered page.

Let’s create something basic, such as a clothing store, and set up routing between the store’s pages. In the pages folder navigate to index.vue and replace the markup there with the following code block:

1// index.vue
2    <template>
3      <div class="clothrack">
4        <h1>Welcome to Hommes, What do you want to sew?</h1>
5        <div class="clothes">
6        <p><nuxt-link to="/shirts">Shirts</nuxt-link></p>
7        </div>
8      </div>
9    </template>
11    <style>
12    .clothrack {
13    font-family: 'Indie Flower', cursive;
14      padding: 50px;
15      width: 100vw;
16      height: 100vh;
17      display: flex;
18      justify-content: space-evenly;
19      flex-wrap: wrap;
20    }
21    .clothes {
22      display: flex;
23      justify-content: space-evenly;
24      flex-wrap: wrap;
25      width: 500px;
26    }
27    </style>

In the pages folder, create another page and call it shirts.vue. In it, insert the following code block:

1// shirts.vue
2    <template>
3      <div class="clothrack">
4        <h1>Get a Shirt Today!</h1>
5        <div class="clothes">
6        <p><nuxt-link to="/">Go back to the Home page</nuxt-link></p>
7        </div>
8      </div>
9    </template>

In the pages folder, create a second page and call it trousers.vue. In it, insert the following code block:

1// trousers.vue
2    <template>
3      <div class="clothrack">
4        <h1>Get Some Trousers Today!</h1>
5        <div class="clothes">
6        <p><nuxt-link to="/">Go back to the Home page</nuxt-link></p>
7        </div>
8      </div>
9    </template>

We want our app to be as extensive as possible. To this end, we’ll create yet another page. In the pages folder, create another page and call it coats.vue. In it, insert the following code block:

1// coats.vue
2    <template>
3      <div class="clothrack">
4        <h1>Get a Coat Today!</h1>
5        <div class="clothes">
6        <p><nuxt-link to="/">Go back to the Home page</nuxt-link></p>
7        </div>
8      </div>
9    </template>

To set up internal routing links between our pages, we’ll use the <nuxt-link/> tag. This tag works just like an a tag, the only difference is that instead of an href it uses to=``"``/page-title-here``". Let’s include the internal routing links for our Trousers page and our Coats page:

1// index.vue
2    <template>
3      <div class="clothrack">
4        <h1>Welcome to Hommes, What do you want to sew?</h1>
5        <div class="clothes">
6        <p><nuxt-link to="/shirts">Shirts</nuxt-link></p>
7        <p><nuxt-link to="/trousers">Trousers</nuxt-link></p>
8        <p><nuxt-link to="/coats">Coats</nuxt-link></p>
9        </div>
10      </div>
11    </template>

Creating a Page Transition

You might have noticed a progress bar running across the top of the screen as we keep routing from page to page. For this demo, we won’t be needing it. In the project’s root folder, navigate to the nuxt.config.js file and change these lines of code:

1// nuxt.config.js
2    /*
3    ** Customize the progress-bar color
4    */
5    loading: { color: '#3B8070' },
10    loading: false,

Aside from the progress bar in the nuxt.config.js file, other things to notice include the meta and head tags as well as the content that will be rendered inside of them. Unlike our normal CLI build with Vue, we won’t have an index.html file. Nuxt breaks down and builds our index.vue file on the server, together with these tags, and then delivers the content for us. Links to fonts and CSS files can be added using the syntax below:

1link: [
2          { rel: '', type: 'text/css', href: '<link to the font or CSS file>' }
3        ]

To create our page transitions, we use Nuxt to plug into the functionality of Vue’s <transition/> component. This component gives us some defaults and hooks that we will work with. To get an in-depth knowledge of how Vue’s transition component works, you can check out this section in the docs here. Check out this image from the docs that best describes the positions at which the transitions start, occur and end:

Source: Vue Animation (

In the image above, there’s a hook for what happens right before the animation starts v-``enter, during the transition from enter to enter-to and when it finishes. There’s also a hook for when the animation is leaving leave, during the transition from leave to leave-to and when it finishes. These hooks enable us to make simple transitions that insert between states. In Vue, the hooks began with v but Nuxt has a different approach, Nuxt hooks begin with page. Let’s create transitions that will plug into the hooks. In our index.vue file, we’ll insert some lines of CSS for the transitions, we’ll also include some extra styling so we can detect the transitions easily:

1<!-- index.vue -->
2    <style>
3    /* Transitions using the page hook */
4    page-enter-active, .page-leave-active {
5      transition: all .30s ease-out;
6    }
7    .page-enter, .page-leave-active {
8      opacity: 0;
9      transform-origin: 50% 50%;
10    }
11    .clothrack {
12    font-family: 'Indie Flower', cursive;
13      padding: 50px;
14      width: 100vw;
15      height: 100vh;
16      display: flex;
17      justify-content: space-evenly;
18      flex-wrap: wrap;
19    }
20    .clothes {
21      display: flex;
22      justify-content: space-evenly;
23      flex-wrap: wrap;
24      width: 500px;
25    }
26    html, body {
27    font-family: 'Indie Flower', cursive;
28      background: #9fdfbf;
29      color: #000000;
30      width: 100vw;
31      height: 100vh;
32    }
33    h1 {
34      width: 1000px;
35      justify-content: center;
36      display: flex;
37    }
38    a, a:visited {
39      color: #ffffff;
40      text-decoration: none;
41    }
42    </style>

Adding multiple page transitions

Right now, all we’ve added is just a CSS transition that flips between our Home page and our other pages; there’s nothing to suggest where a page could be coming from or going to. Let’s create a different transition between our Home page and our Shirts page. In our shirts.vue file, we’ll use CSS animations to specify the direction in which our pages are coming from and going to, then plug into each of .page-enter-active, and .page-leave-active:

1<!-- shirts.vue -->
2    <style scoped>
3    .page-enter-active {
4      animation: acrossIn .40s ease-out both;
5    } 
6    .page-leave-active {
7      animation: acrossOut .60s ease-in both;
8    } 
9    @keyframes acrossIn {
10      0% {
11        transform: translate3d(-100%, 0, 0);
12      }
13      100% {
14        transform: translate3d(0, 0, 0);
15      }
16    }
17    @keyframes acrossOut {
18      0% {
19        transform: translate3d(0, 0, 0);
20      }
21      100% {
22        transform: translate3d(100%, 0, 0);
23      }
24    }
25    .clothrack {
26      font-family: 'Indie Flower', cursive; 
27      padding: 50px;
28      width: 100vw;
29      height: 100vh;
30    }
31    .clothes {
32      display: flex;
33      justify-content: space-evenly;
34      flex-wrap: wrap;
35      width: 500px;
36    }
37    html, body {
38      font-family: 'Indie Flower', cursive;
39      background: #9fdfbf;
40      color: #000000;
41      width: 100vw;
42      height: 100vh;
43    }
44    h1 {
45      width: 1000px;
46      justify-content: center;
47      display: flex;
48    }
49    a, a:visited {
50      color: #ffffff;
51      text-decoration: none;
52    }
53    </style>

The function of the scoped tag is to limit the styles to be applied to this page only. Let’s give shirts.vue a different text color so we can tell the difference:

2    <style scoped>
3      .clothrack {
4        color: #595959;
5      }
6    </style>

Okay let’s say, for some of us, that was way too much transition, we want something basic, say a fade out. To create separate transitions, in the .vue file where we want a different transition, we rename our transition hook just to set it out:

2    export default {
3      transition: '<transition hook name>'
4    }
5    </script>

Let’s create a fade out transitions for our Trousers and Coats page. In the pages folder, navigate to the trousers.vue file and insert this block of code:

2    <script>
3    export default {
4      transition: 'tweakOpacity'
5    }
6    </script>

We have given the transition for our Trousers page a new identity, let’s proceed to insert the animations for our Trousers page:

2    <style>
3    .tweakOpacity-enter-active, .tweakOpacity-leave-active {
4      transition: opacity .40s ease-out;
5    }
6    .tweakOpacity-enter, .tweakOpacity-leave-active {
7      opacity: 0;
8    }
9    .clothrack {
10      font-family: 'Indie Flower', cursive;
11      padding: 50px;
12      width: 100vw;
13      height: 100vh;
14      display: flex;
15      flex-wrap: wrap;
16      justify-content: center;
17    }
18    .clothes {
19      display: flex;
20      justify-content: space-evenly;
21      flex-wrap: wrap;
22      width: 500px;
23    }
24    html, body {
25    font-family: 'Indie Flower', cursive;
26      background: #9fdfbf;
27      color: #000000;
28      width: 100vw;
29      height: 100vh;
30    }
31    h1 {
32      width: 1000px;
33      justify-content: center;
34      display: flex;
35    }
36    a, a:visited {
37      color: #ffffff;
38      text-decoration: none;
39    }
41    </style>

When we are through with our Trousers page, we perform the same actions on our Coats page. It’s possible to go even further and create more CSS animations for every new page. Let’s check out what our app looks like now:


It’s very clear that working with Nuxt offers more possibilities and ways of building projects in an easier, less daunting way. Animations and transitions should be considered when building user interfaces as they say a thousand words about your work. The Nuxt.js documentation is a good place to start. Throwing Vue into your shopping basket wouldn’t be a bad bargain. Happy coding!