Share this:

Some of the most inspiring examples I’ve seen of front-end development have involved some sort of page transitions that look slick like they do in mobile apps. However, even though the imagination for these types of interactions seem to abound, their presence on actual sites that I visit do not. There are a number of ways to accomplish these types of movement!

We’ll build out the simplest possible distillation of these concepts so that you can apply them to any application, and then I’ll also provide the code for this more complex app if you’d like to dive in.

Today we’ll be discussing how to create them with Vue and Nuxt. There are a lot of moving parts in page transitions and animations (lol I kill me), but don’t worry! Anything we don’t have time to cover in the article, we’ll link off to with other resources.

Why?

The web has come under critique in recent years for appearing "dated" in comparison to native iOS and Android app experiences. Transitioning between two states can reduce cognitive load for your user, as when someone is scanning a page, they have to create a mental map of everything that's contained on it. When we move from page to page, the user has to remap that entire space. If an element is repeated on several pages but altered slightly, it mimics the experience we've been biologically trained to expect — no one just pops into a room or changes suddenly; they transition from another room into this one. Your eyes see someone that's smaller relative to you. As they get closer in proximity to you, they get bigger. Without these transitions, changes can be startling. They force the user to remap placement and even their understanding of the same element. It is for this reason that these effects become critical in an experience that helps the user feel at home and gather information quickly on the web.

The good news is, implementing these kind of transitions is completely doable. Let's dig in!

Prerequisite Knowledge

If you’re unfamiliar with Nuxt and how to work with it to create Vue.js applications, there’s another article I wrote on the subject here. If you’re familiar with React and Next.js, Nuxt.js is the Vue equivalent. It offers server-side rendering, code splitting, and most importantly, hooks for page transitions. Even though the page transition hooks it offers are excellent, that’s not how we’re going to accomplish the bulk of our animations in this tutorial.

In order to understand how the transitions we’re working with today do work, you’ll also need to have basic knowledge around the <transition /> component and the difference between CSS animations and transitions. I’ve covered both in more detail here. You’ll also need basic knowledge of the <transition-group /> component and this Snipcart post is a great resource to learn more about it.

Even though you’ll understand everything in more detail if you read these articles, I’ll give you the basic gist of what’s going on as we encounter things throughout the post.

Getting Started

First, we want to kick off our project by using the Vue CLI to create a new Nuxt project:

Great! Now the first thing you’ll notice is that we have a pages directory. Nuxt will take any .vue files in that directory and automatically set up routing for us. Pretty awesome. We can make some pages to work with here, in our case: about.vue, and users.vue.

Setting Up Our Hooks

As mentioned earlier, Nuxt offers some page hooks which are really nice for page to page transitions. In other words, we have hooks for a page entering and leaving. So if we wanted to create an animation that would allow us to have a nice fade from page to page, we could do it because the class hooks are already available to us. We can even name new transitions per page and use JavaScript hooks for more advanced effects.

But what if we have some elements that we don’t want to leave and re-enter, but rather transition positions? In mobile applications, things don’t always leave when they move from state to state. Sometimes they transition seamlessly from one point to another and it makes the whole application feel very fluid.

Step One: Vuex Store

The first thing we’ll have to do is set up a centralized state management store with Vuex because we’re going to need to hold what page we’re currrently on.

Nuxt will assume this file will be in the store directory and called index.js:

We’re storing both the page and we create a mutation that allows us to update the page.

Step Two: Middleware

Then, in our middleware, we’ll need a script that I’ve called pages.js. This will give us access to the route that’s changing and being updated before any of the other components, so it will be very efficient.

We’ll also need to register the middleware in our nuxt.config.js file:

module.exports = {
...
router: {
middleware: 'pages'
},
...
}

Step Three: Register Our Navigation

Now, we’ll go into our layouts/default.vue file. This directory allows you to set different layouts for different page structures. In our case, we’re not going to make a new layout, but alter the one that we’re reusing for every page. Our template will look like this at first:

<template>
<div>
<nuxt/>
</div>
</template>

And that nuxt/ tag will insert anything that’s in the templates in our different pages. But rather than reusing a nav component on every page, we can add it in here and it will be presented consistently on every page:

This is also great for us because it won’t rerender every time the page is re-routed. It will be consistent on every page and, because of this, we cannot plug into our page transition hooks but instead we can build our own with what we built between Vuex and the Middleware.

Step Four: Create our Transitions in the Navigation Component

Now we can build out the navigation, but I’m also going to use this SVG here to do a small demo of basic functionality we’re going to implement for a larger application

We’re doing a few things here. In the script, we bring in the page name from the store as a computed value. mapState will let us bring in anything else from the store, which will handy later when we deal with a lot of user information.

In the template, we have a regular nav with nuxt-links, which is what we use for routing links in Nuxt. We also have class that will be updated on a conditional based on the page (it will change to .active when it’s the about page.

We’re also using the <transition-group> component around a number of elements that will change positions. The <transition-group> component is a bit magical because it applies the concepts of FLIP under the hood. If you’ve heard of FLIP before, you’re going to be super excited to hear this because it’s a really performant way of animating on the web but usually takes a lot of calculations to implement. If you haven’t heard of FLIP before, it’s definitely good to read up to understand how it works, and maybe more importantly, all of the stuff you no longer have to do to make this kind of effect work! Can I get a "Hell yeah!"

Here is the CSS that makes this work. We basically state how we’d like all of the elements to be positioned on that “active” hook that we made. Then we tell the elements to have a transition applied if something changes. You'll notice I'm using 3D transforms even if I'm just moving something along one X or Y axis because transforms are better for performance than top/left/margin for reducing paint and I want to enable hardware acceleration.

I want to point out that any implementations I use here are choices that I've made for placement and movement- you can really create any effect you like! I am choosing SVG here because it communicates the concept of layout in a small amount of code, but you don't need to use SVG. I'm also using transitions instead of animation because of how declarative they are by nature- you are in essence stating: "I want this to be repositioned here when this class is toggled in Vue", and then the transition's only job is to describe the movement as anything changes. This is great for this use-case because it's very flexible. I can then decide to change it to any other conditional placement and it will still work.

Great! This will now give us the effect, smooth as butter between pages, and we can still give the content of the page a nice little transition as well:

Middleware commits a mutation to let the store know the page has changed.

We apply a special class per page, and nest transitions for each page.

The navigation stays consistent on each page but we have different positions and apply some transitions.

The content of the page has a subtle transition and we build in some interactions based on user events

The only difference is that this is a slightly more involved implementation. The CSS that's applied to the elements will stay the same in the navigation component. We can tell the browser what position we want all the elements to be in, and since there's a transition applied to the element itself, that transition will be applied and it will move to the new position every time the page has changed.

That’s it! We keep it nice and simple and use flexbox, grid and absolute positioning in a relative container to make sure everything translates easily across all devices and we have very few media queries through this project. I’m mainly using CSS for the nav changes because I can declaratively state the placement of the elements and their transitions. For the micro-interactions of any user-driven event, I’m using JavaScript and GreenSock, because it allows me to coordinate a lot of movement very seamlessly and stabilizes transform-origin across browsers, but there are so many ways you could implement this. There are a million ways I could improve this demo application, or build on these animations, it's a quick project to show some possibilities in a real-life context.

Remember to hardware accelerate and use transforms, and you can achieve some beautiful, native-like effects. I’m excited to see what you make! The web has so much potential for beautiful movement, placement, and interaction that reduces cognitive load for the user.

Share this:

Comments

Hello Sarah, thanks for this tutorial.
Step 4 is missing some information, which were a little bit confusing for me as a total vue noob:
You should mention that the contents (HTML, JavaScript, SCSS and CSS) should go into the file components/AppNavigation.vue
It would be good to write that for SCSS compilation you need to put the code betweeen
tags in the same .vue file.

Hi there, this is why I mention the prerequisite section- this article does assume some basic familiarity and if you don’t have that, it would be good to read through the other materials first, it might be a richer experience. All of the full .vue files are posted in both github repos that are linked up! Both demos are full applications so it would take too long to walk through each piece of everything, and most of it is besides the point of the main concepts. Also highly recommend Egghead’s Nuxt courses if you’re trying to learn.

Hey there, me again. I also realised, that there are errors in the SCSS code. You used translateX with 3 parameters, but translateX only expects one, this is why the animation doesn’t work correctly. However you could use translate3d instead.

Thanks for this, I have been just starting to look into Vue, and this certainly intrigues me even more

Obviously this works best with flat files — have you hooked anything like this up to a CMS? Have you been happy with the way it loads dynamic content? (I ask this as someone who works with CMSes, but is new to JS frameworks…)

I’m really happy to see this topic gaining traction, as it’s something I have been missing personally in current web development discurse. I will try to strengthen my nuxt-skills to better understand your implementation

Do you have further examples somewhere, where you are applying your technique? I’d love so much to see “complex” transformations whilst transitioning, master-detail-view changes, …

In this case, the profile icon, for example, is kept common outside the about page. This approach requires shared elements to be pulled out and kept in a higher level. Doing this will add one level nesting.

It would be nice if we could mark a div in two different Vue components with some common id like “anim-ref”, and Vue would understand that the same div is to be retained for render across these vue components :)

I’m new to the world of CSS animations – some of the animations are a little choppy/stilted in Firefox (59.0.2, Linux) but perfectly smooth in Chromium. Is Firefox’s animation support not up to par with Chromium (if so, are there certain types of transitions/animations to avoid?) or does it seem like an issue with my machine/setup?

Yep, I noticed the same thing, and a few weeks before the launch of this article, I showed the firefox team. This isn’t actually the first time I’ve noticed animations lagging in that browser, as well as some other web animation friends noticing things have been pretty slow since the update. They’re said they’re looking into it and the browser is still working on this kind of rendering, so hopefully it will improve!

We’re actually working on an article to demonstrate this! More soon. The big issue isn’t really middleware as much as the fact that React doesn’t have an equivalent to transition-group, so it’s a little easier to accomplish with Vue. But not impossible! Stay tuned.

Thanks for sharing this article. I’m looking into migrating my company’s website from React to Vue, basically just for learning purposes, and we use a lot of full page transitions like this. This will be very helpful to me.

We’re actually already hardware accelerating, so will-change probably wouldn’t help much here. Aside from that, I stay away from it these days. It has a few known issues, and can cause a lot of problems with images. More info here: https://greensock.com/will-change

Hi! Yep- actually some of the microinteractions already use GSAP :) I am using CSS here so that everyone can understand how it’s working, and use what they like (the purpose of the demo is to lower the barrier to entry for all audiences). I have found that GSAP performed way better on Firefox, and it’s something I’ve shared with their team. One thing that’s nice about using transitions is that it’s pretty easy to state what it should be for each page and then keep the motion consistent, but really the sky’s the limit!

Sorry, what about the title communicated CSS only? I have always been bad at naming things :)

fwiw, there’s another article on here that uses jquery for similar effects if you’re able to use jquery. css only you could definitely have some slight of hand on entrance, but it’s much harder to connect them.

Page transitions is something I’ve always wanted to gronk better; being a “learn the fundamentals before abstractions” kind of learner, is there a chance there will a variant of this guide that is a “lodge” series/course/guide to implement page transitions without any framework?

This comment thread is closed. If you have important information to share, please contact us.

Related

How do you stay up to date in this fast⁠-⁠moving industry?

A good start is to sign up for our weekly hand-written newsletter. We bring you the best articles and ideas from around the web, and what we think about them.

👋

CSS-Tricks* is created, written by, and maintained by Chris Coyier and a team of swell people. It is built on WordPress and powered up by Jetpack. It is made possible through sponsorships from products and services we like.