Create smooth page transitions with Barba.js

Page transitions can greatly impact the overall user experience for a website or application. A seamless and effortless flow between webpages can be helpful in maintaining a user’s focus and interest. Conversely, clunky or slow transitions can be a major turnoff, resulting in a higher bounce rate and a reduction in engagement and conversions.

In this article, we’ll discuss improving user experience by creating smooth page transitions with Barba.js, a popular JavaScript library.

Jump ahead:

What is Barba.js?

Barba.js is a JavaScript library that handles the transition between webpages, allowing the user to experience a seamless page change with an animation, rather than a jarring browser reload with a blank page. This improves the overall look and feel of the website and enhances user experience.

Barba.js operates by updating the content of a page dynamically, without having to reload the entire page. This results in a faster and more seamless experience for the user, as they can move between pages without any interruptions or delays. The library is designed to be lightweight, making it easy to implement and use in a wide range of projects.

A key feature of Barba.js is its ability to cache pages, ensuring a fast loading experience — even when a user navigates back and forth between pages. It also provides a range of customization options, allowing developers to create unique and tailored page transitions. In addition, the library supports the preloading of pages, providing a smooth and uninterrupted experience, even when a user clicks on a link.

Setting up Barba.js

Let’s look at the different ways we can bring Barba.js into our project.

Installation

We can install Barba.js with a bundler or via a CDN.

Via a bundler

Barba.js is available on the npm registry and can be installed through the command line using a package manager such as npm or yarn:

# npm
npm install @barba/core

# yarn
yarn add @barba/core

Then, you can import it into your project like any other module:

import barba from '@barba/core';

Via a CDN

The fastest way to use Barba.js in your project is to simply load the latest build from your favorite CDN; this approach requires minimal setup:

// HTML
<!-- unpkg -->
<script src="https://unpkg.com/@barba/core"></script>

<!-- jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/@barba/core"></script>

Next, init Barba.js, like so:

// HTML
<script>
  barba.init({
    // ...
  })
</script>

You can also use an external script file and just link it:

// HTML
<script src="https://blog.logrocket.com/create-smooth-page-transitions-barba-js/./app.js"></script>

Markup

Barba.js needs to understand your site architecture so that it knows which elements to swap and which elements to retain.
Here’s how your markup should look:

// HTML
<body data-barba="wrapper">

<!-- You can have contents that will not be swapped in here, like <div class="navbar"> -->

<main data-barba="container" data-barba-namespace="home">
<!-- put here the content you wish to swap -->
</main>

<!-- You can have contents that will not be swapped in here, like <footer> -->
</body>

Let’s take a closer look at the Barba.js attributes:

  • wrapper: Central component of Barba.js that holds the entire page structure. Any elements within the barba-wrapper, but outside of the barba-container, will not be swapped during transition, making it a safe area to place items such as headers, footers, or navigation bars. The wrapper is typically placed on the <body> tag, but it can also be placed within a <div> or <section> element
  • container: **Section of the page that gets swapped during transition; this is where you should place the <main> content of a particular page. The container is typically defined within the <main> tag, but it can also be placed within a <div> or <section> element. One thing to note is that Barba.js appends the container to the wrapper, meaning it will always be at the bottom of the wrapper
  • namespace: Provides the ability to assign a unique name to each page, which can then be used to create specific transition rules for each page, if desired

Understanding Barba.js transition logic

Barba.js alters the behavior of a website making it work like a single page application and thereby enabling the library to create smooth transitions without having to reload the entire site.

Here are the three steps of the Barba.js lifecycle when navigating between two pages:

  1. On mouseover or touchstart events, Barba.js pre-fetches and caches the next page’s content in the background, so that the transition between pages is seamless and fast
  2. The data-barba=container of the next page is added to the end of the element with the attribute "data-barba="wrapper"
  3. Once the transition is completed, the element with the data-barba=container attribute is removed from the DOM

Barba.js provides us with different hooks at different stages of the transition lifecycle. One example is the afterLeave hook, which enables us to perform any necessary post-transition actions, such as updating the page’s title:

Great! Now that we have all the setup and transition logic covered, let’s build a simple example so that we can really see Barba.js in action.

Implementing page transitions

Barba.js is a transition library, not an animation library. You’ll still need an animation library, like GSAP, to handle animations.

The transition consists of two parts:

  • leave () animation: Takes place when leaving the current page
  • enter () animation: Occurs when entering the next page

Let’s get started!

First, we’ll need to set up a new project. Our example will be built with plain HTML, CSS, and JavaScript.

Here’s what our project folder structure should look like:

- Root folder
  |
  |- index.html
  |- about.html
  |- app.js
  |- styles
  |   |
  |   |- main.scss

Barba.js Project Folder Structure

Once our project folder structure is complete, we’ll need to add two libraries, GSAP and Barba.js, into our project. For this example, we’ll use a CDN to import both libraries.

Here’s what that will look like for each page:

<!DOCTYPE html>
<html lang="en">
      <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta
                  name="viewport"
                  content="width=device-width, initial-scale=1.0"
            />
            <title>Barba-demo Home</title>
            <link rel="stylesheet" href="https://blog.logrocket.com/create-smooth-page-transitions-barba-js/main.css" />
      </head>
      <body data-barba="wrapper">
            <main data-barba="container"></main>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script>
            <script src="https://cdn.jsdelivr.net/npm/@barba/core"></script>
            <script src="app.js"></script>
      </body>
</html>

In both the index.html and about.html pages, the custom stylesheet is linked at the <head> element, and the libraries are imported through CDN links in the <body> element.

In the code above, we inform Barba.js about the structure of our website by using the attributes data-barba="wrapper" and data-barba="container". These attributes are applied to each page of our site so that Barba.js knows what part of each webpage to swap.

Here’s a CodeSandbox with the entire project:

suspicious-architecture-l6mqlt

suspicious-architecture-l6mqlt by Nelson Michael using @barba/core, gsap, parcel-bundler, sass

Here’s the entire code for the index.html page:

<!DOCTYPE html>
<html lang="en">
      <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta
                  name="viewport"
                  content="width=device-width, initial-scale=1.0"
            />
            <title>Barba-demo Home</title>
            <link rel="stylesheet" href="https://blog.logrocket.com/create-smooth-page-transitions-barba-js/styles/main.css" />
      </head>
      <body data-barba="wrapper">
            <div class="page-transition-container">
                  <span class="page-transition"></span>
                  <span class="page-transition"></span>
            </div>
            <nav>
                  <p>Wivalow</p>
                  <ul>
                        <li><a href="http://blog.logrocket.com/index.html">Home</a></li>
                        <li><a href="http://blog.logrocket.com/about.html">About</a></li>
                  </ul>
            </nav>
            <main data-barba="container">
              <div class="img-container">
                <img src="https://images.unsplash.com/photo-1675873725360-b15df4950952?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=465&q=80" alt=""/>
                  <div class="bg-underlay"></div>
                  </div>
                  <div class="desc">
                    <h1>Top<br />Echelon<br />Luxury</h1>
                    <p>Experience the epitome of luxury and style with Wivalow.<br> Shop with us and elevate your wardrobe to new heights.</p>
                  </div>
            </main>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.11.4/gsap.min.js"></script>
            <script src="https://cdn.jsdelivr.net/npm/@barba/core"></script>
            <script src="app.js"></script>
      </body>
</html>

Our about.html page is exactly the same; we’re only changing the images, just for the sake of distinction.


More great articles from LogRocket:


The index.html file includes the below code. This is the element that will be animated during transition. We’re using the element as an overlay over the entire page:

<div class="page-transition-container">
    <span class="page-transition"></span>
    <span class="page-transition"></span>
</div>

Here’s the CSS for the above overlay code:

.page-transition-container{
      display: flex;
      flex-direction: column;
      position: absolute;
      z-index:15;
      height:100vh;
      width:100%;
      top:0;
      left:0;
      margin:0;
      pointer-events: none;
      .page-transition{
            transform: scaleX(0);
            background: #2c2c2c;
            height:50%;
      }
}

Now, let’s get to the main bit of our code.

I’m going to summarize the process in just two steps: defining a function to handle the animation with GSAP and defining a leave() transition with Barba.js.

Defining a function to handle the animation with GSAP

First, we define a transition function that uses GSAP to animate our overlay element that has a class of .page-transition:

const transition=()=>{
      let tl = gsap.timeline();
      tl.to('.page-transition', { duration: .5, scaleX:1, transformOrigin: "bottom right", stagger: .2})
      tl.to('.page-transition', { duration: .5, scaleX: 0, transformOrigin: "bottom right", stagger: .1 , delay:.1})
}

The animation consists of two parts: first, the element scales up on the x-axis over 0.5s with a stagger of 0.2s; then it scales down over 0.5s with a stagger of 0.1s and a delay of 0.1s.

Defining a leave() transition with Barba.js

Next, we initialize Barba.js and define a leave() transition within an object inside the transitions array:

barba.init({
        transitions: [{
            leave(){
                  transition();
            }
      }]
})

Barba.js offers various hooks that can be utilized to initiate a transition animation at various stages during the page change process. Here’s a comprehensive list of the available hooks.

The leave() hook instructs Barba.js to immediately switch the content of the current page, defined by the element with the data-barba="container" attribute, with the content of the next page while animating the overlay element to create a smooth page transition.

Now any time we leave a page, we have a smooth transition animation, instead of a hard page reload.

What if we want a different kind of animation for specific pages? Well, we can easily achieve that by setting a data-barba-namespace="unique-name" attribute on the element with the data-barba="container" attribute. We can now reference that name in the transitions array, like so:

barba.init({
        transitions: [{
            name: "unique-name"
            leave(){
                  differentTransition();
            }
      }, 
      {
          name: "another-unique-name"
          enter(){
                anotherDifferentTransition();
        }
      }
]
})

Defining specific transitions (transition resolution)

What if we want to specify different types of animation for specific pages? Well, we can easily achieve that with transition rules. We can set a data-barba-namespace="unique-name" attribute on the element with the data-barba="container" attribute and then reference that name in the transitions array, like so:

// JavaScript

barba.init({
        transitions: [{
            namespace: "unique-name"
            leave(){
                  differentTransition();
            }
      }, 
      {
          namespace: "another-unique-name"
          enter(){
                anotherDifferentTransition();
        }
      }
]
})

But here’s the real gist of the Barba.js transition resolution feature — by using rules, we can define specific transitions based on a namespace, route, or custom logic to be initiated when a user visits a specific page.

The transition resolution rules are made up of keywords and conditions.

Think of rules as simple if statements:

if(condition is met){
  runTransition()
}

Let’s take a closer look at this feature.

Keywords

Keywords simply define our logic flow. For example, we could use the from keyword to indicate that the transition should be triggered when a user leaves the current page. Or, we could use the to keyword to indicate that the transition should occur when the user enters the next page. We could even use both keywords in one transition:

barba.init({
  transitions: [

    {
      from: {
        //
      },
      leave() {
        transition();
      }
    }


  ]
});

Keywords and their conditions are fulfilled based on priority. This impacts how the keywords should be used and what precedence they will take:

Keyword Priority
to and from high
to medium
from low

Conditions

Conditions define the logic that must be fulfilled before a transition can be carried out. There are three kinds of conditions that we can declare:

  • custom: We provide Barba.js with a custom function that returns either true or false; the transition only runs if true is returned
  • route: When using the Barba.js router, we can use this condition to check that the route matches
  • namespace: We tell Barba.js to only trigger the transition if the declared namespace matches. For more context, if a transition is declared with the from keyword, Barba.js checks that the namespace provided matches the namespace declared on the current page; if a transition is declared with the to keyword, Barba.js ensures that it matches the namespace of the next page

We can also declare multiple conditions and Barba.js will apply a transition if all conditions are met:

barba.init({
  transitions: [
    {
      //keyword
      from: {
        // namespace condition
        namespace: ["home"]
        // or route condition
        route: ["index"]
        // or custom condition
      custom: ({ trigger }) => {
        return trigger.classList && trigger.classList.contains('use-custom-transition');
      },
      },
      // transition that would run only if conditions are met
      leave() {
        transition();
      }
    }
  ]
});

N.B., in the above example, thecustom condition code was selected from the Barba.js docs. Here, trigger is the link that prompts the transition

It’s also important to note a condition’s priority. Just like keywords, some conditions will take precedence over others due to their higher priority:

Condition Priority
custom high
route medium
namespace low

Conclusion

Barba.js is a powerful tool for creating smooth page transitions and enhancing user experience on your website. With its easy setup and customizable options, Barba.js provides a wide range of options to create transitions that match your specific needs. Whether you’re looking to add a touch of animation to your site or simply make the flow between pages more seamless, Barba.js is an excellent choice.

Are you adding new JS libraries to improve performance or build new features? What if they’re doing the opposite?

There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.

LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.


https://logrocket.com/signup/

LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Build confidently — .


Source link