DEV Community

Cover image for How to create beautiful view transitions in Nuxt using the new View Transitions API
Michał Kuncio
Michał Kuncio

Posted on • Originally published at michalkuncio.com

How to create beautiful view transitions in Nuxt using the new View Transitions API

Intro

The Web platform is thriving with all those new APIs that were introduced lately. Both JS and CSS are developing at a fantastic pace and allow us to achieve certain effects much easier and more pleasantly than we have done so far. One of the most interesting APIs that have recently gained wider support is the View Transitions API.

What is Web Transitions API?

Web Transitions API is a new API that gives us a better way of creating animated transitions between different DOM states while also updating the DOM contents in a single step. Doing it manually in SPA required writing a lot of CSS and JS and required all HTML content from two pages at the moment of the transition. This caused some issues and was not a perfect solution in terms of accessibility. With the new API, it's much easier to create stunning mobile-like transitions between app states which are more accessible and performant. Currently, it works for Chromium-based browsers and this is how view-transition-name which is all you need is supported now:

Support

Of course, this feature has to be supported by your favorite framework. We are lucky because Nuxt was the first framework that gave us the ability to try this new and amazing feature! Please take into account that this is still an experimental feature.

Create stunning view transition in Nuxt

First of all, please check the live demo on my very own website. For now, you have to use Chrome to notice the effects.

When you click one of the articles on the homepage, you can observe a really cool animation of some elements inside the article card. Here is low-res GIF, but it's better to see it live to better understand all interactions:

Gif

As you can see, that are 4 elements involved in the transition:

  • article cover image
  • article title
  • author
  • date

Every single element mentioned above is making its own transition from the position on the homepage to the position on the article page.

Let's check how we can achieve it with Nuxt!

Enabling View Transitions in Nuxt

To use View Transitions in Nuxt you have to enable it in nuxt.config file:

// nuxt.config.ts
export default defineNuxtConfig({
    // ...
    experimental: {
        viewTransition: true,
        // ...
    },
    // ...
});

Enter fullscreen mode Exit fullscreen mode

Adding view transitions in (S)CSS

Now we have to make a connection between the same elements on pages we want to make a transition by setting the matching view transition names. I want to make a transition whenever a user will click on one of the article cards:

Article card

If our article card is wrapped in article tag and we would like to create a view transition name for the image cover inside our card we can do it that way in SCSS:

article {
    img {
        view-transition-name: article-thumb;
    }
}
Enter fullscreen mode Exit fullscreen mode

Ok, so we informed CSS that we want to apply view transition for the article image and named it accordingly. Now we have to find the article image on the article page itself.

Article page

Let's do the same thing for that element. Since I have it in a separate component with scoped styles and we have to do is:

img {
    view-transition-name: article-thumb;
}

Enter fullscreen mode Exit fullscreen mode

Now, let's do the same for the article title, date and author:

CSS in homepage ArticleCard component:

article {
    img {
        view-transition-name: article-thumb;
    }

    .article-title {
        view-transition-name: article-title;
    }

    .author-info {
        view-transition-name: author;
    }

    .date {
        view-transition-name: date;
    }
}
Enter fullscreen mode Exit fullscreen mode

CSS in article ArticleHeader component.

img {
    view-transition-name: article-thumb;
}

.article-title {
    view-transition-name: article-title;
}

.date {
    view-transition-name: date;
}

.article-thumb {
    border-radius: 20px;
    max-width: 100%;
}
Enter fullscreen mode Exit fullscreen mode

How does it work? By matching view-transition-name. During navigation, it checks if there are view-transition-names with the same name on both pages and applies view-transition at the exact time of route change. But wait! How this would work if we have let's say 20 articles on the homepage? It means that we have 20 elements with the same page-transition-name right? It won't work that way, because the browser will not know which element to use in the transition. We need to somehow inform the browser that we only want to involve clicked article in the transition. There are many ways to do it but I like to apply the .active class on the clicked article card and set the view transition only for article cards with .active class.

The CSS for the homepage would look like that:

article {
    &.active {
        img {
            view-transition-name: article-thumb;
        }

        .article-title {
            view-transition-name: article-title;
        }

        .author-info {
            view-transition-name: author;
        }

        .date {
            view-transition-name: date;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

We don't have to do the same thing for ArticleHeader because when you will navigate to a single article there is only one title, cover image, date and author. So you only have to do it if you potentially have many elements with the same view-transition-name from which you have to make a transition.

Now we have to programmatically add those classes to clicked Article Card.

Active article logic

On my blog, I'm using Pinia for state management so we can use it for our active article logic. Let's create a new store for articles:

import { defineStore } from 'pinia';
// stores/article.ts
export const useArticlesStore = defineStore('articles', () => {
    const activeArticle = ref(null);
    function setActiveArticle (article) {
        activeArticle.value = article;
    }

    return { activeArticle, setActiveArticle };
});

Enter fullscreen mode Exit fullscreen mode

This store is really simple. It stores the whole reference to the active article and has a setter for setting the active article.

Now in our ArticleCard component, we have to import the store:

// ArticleCard.vue
import { useArticlesStore } from '@/stores/articles';
const articlesStore = useArticlesStore();
Enter fullscreen mode Exit fullscreen mode

On every click on every link in the ArticleCard, we have to set the clicked article as active. We can do it that way for clickable article thumb:

<!-- ArticleCard.vue -->
<NuxtLink class="article-thumb-wrapper" :to="article._path" @click="articlesStore.setActiveArticle(article)">
    <div class="article-thumb">
        <nuxt-img :src="article.thumb" />
    </div>
</NuxtLink>
Enter fullscreen mode Exit fullscreen mode

Now, when a specific article card will be clicked, this article will be marked as active inside our articles store. All we need to make to finish the work on this is to add the active class on the active article card wrapping element:

<article class="article-item" :class="{active: articlesStore.activeArticle && article._id === articlesStore.activeArticle._id}">
Enter fullscreen mode Exit fullscreen mode

It conditionally applies the active class when the id of the article in ArticleCard is matching the ID of the active article in the articles store.

More context:

    <article class="article-item" :class="{active: articlesStore.activeArticle && article._id === articlesStore.activeArticle._id}">
        <NuxtLink class="article-thumb-wrapper" :to="article._path" @click="articlesStore.setActiveArticle(article)">
            <div class="article-thumb">
                <nuxt-img :src="article.thumb" />
            </div>
        </NuxtLink>
        <!-- other elements -->
    </article>
Enter fullscreen mode Exit fullscreen mode

And now it should work!

Summary

View Transitions API is fantastic addition to freshly introduced web APIs. In this article, I showed the simplest solution to create visually stunning. You can customize view-transitions and have more fine-grained control by JavaScript. I encourage you to review the documentation to learn more. What's cool is that even though not all browsers support it, using it now won't break anything in browsers that don't support it. And when they start, it will start working! I'm pretty sure that in the nearest future, many browsers will implement view transitions because it can improve user experience and create mobile-like, native feeling when using web browsers.

Top comments (0)