barba-js

Page transitions library for creating fluid, smooth transitions between website pages. Use this skill when implementing page transitions, creating SPA-like…

INSTALLATION
npx skills add https://github.com/freshtechbro/claudedesignskills --skill barba-js
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

1. Wrapper, Container, and Namespace

Barba.js uses a specific DOM structure to manage transitions:

HTML Structure:

<body data-barba="wrapper">

  <!-- Static elements (header, nav) stay outside container -->

  <header>

    <nav>

      <a href="/">Home</a>

      <a href="/about">About</a>

    </nav>

  </header>

  <!-- Dynamic content goes in container -->

  <main data-barba="container" data-barba-namespace="home">

    <!-- This content changes on navigation -->

    <h1>Home Page</h1>

    <p>Content that will transition out...</p>

  </main>

  <!-- Static footer outside container -->

  <footer>© 2025</footer>

</body>

Three Key Elements:

-

Wrapper (data-barba="wrapper")

  • Outermost container
  • Everything inside wrapper but outside container stays persistent
  • Ideal for headers, navigation, footers that don't change

-

Container (data-barba="container")

  • Dynamic content area that updates on navigation
  • Only this section gets replaced during transitions
  • Must exist on every page

-

Namespace (data-barba-namespace="home")

  • Unique identifier for each page type
  • Used in transition rules and view logic
  • Examples: "home", "about", "product", "blog-post"

2. Transition Lifecycle

Barba.js follows a precise lifecycle for each navigation:

Default Async Flow:

  • User clicks link
  • Barba intercepts navigation
  • Prefetch next page (via AJAX)
  • Cache new content
  • Leave hook - Animate current page out
  • Wait for leave animation to complete
  • Remove old container, insert new container
  • Enter hook - Animate new page in
  • Wait for enter animation to complete
  • Update browser history

Sync Flow (with sync: true):

  • User clicks link
  • Barba intercepts navigation
  • Prefetch next page
  • Wait for new page to load
  • Leave and Enter hooks run simultaneously (crossfade effect)
  • Swap containers
  • Update browser history

3. Hooks

Barba provides 11 lifecycle hooks for controlling transitions:

Hook Execution Order:

Initial page load:

  beforeOnce → once → afterOnce

Every navigation:

  before → beforeLeave → leave → afterLeave →

  beforeEnter → enter → afterEnter → after

Hook Types:

  • Global hooks: Run on every transition (barba.hooks.before())
  • Transition hooks: Defined within specific transition objects
  • View hooks: Defined within view objects for page-specific logic

Common Hook Use Cases:

  • beforeLeave - Reset scroll position, prepare animations
  • leave - Animate current page out
  • afterLeave - Clean up old page
  • beforeEnter - Prepare new page (hide elements, set initial states)
  • enter - Animate new page in
  • afterEnter - Initialize page scripts, analytics tracking

4. Views

Views are page-specific logic containers that run based on namespace:

barba.init({

  views: [{

    namespace: 'home',

    beforeEnter() {

      // Home-specific setup

      console.log('Entering home page');

    },

    afterEnter() {

      // Initialize home page features

      initHomeSlider();

    }

  }, {

    namespace: 'product',

    beforeEnter() {

      console.log('Entering product page');

    },

    afterEnter() {

      initProductGallery();

    }

  }]

});

Common Patterns

1. Basic Setup

Installation:

npm install --save-dev @barba/core

# or

yarn add @barba/core --dev

Minimal Configuration:

import barba from '@barba/core';

barba.init({

  transitions: [{

    name: 'default',

    leave({ current }) {

      // Fade out current page

      return gsap.to(current.container, {

        opacity: 0,

        duration: 0.5

      });

    },

    enter({ next }) {

      // Fade in new page

      return gsap.from(next.container, {

        opacity: 0,

        duration: 0.5

      });

    }

  }]

});

2. Fade Transition (Async)

Classic fade-out, fade-in transition:

import barba from '@barba/core';

import gsap from 'gsap';

barba.init({

  transitions: [{

    name: 'fade',

    async leave({ current }) {

      await gsap.to(current.container, {

        opacity: 0,

        duration: 0.5,

        ease: 'power2.inOut'

      });

    },

    async enter({ next }) {

      // Start invisible

      gsap.set(next.container, { opacity: 0 });

      // Fade in

      await gsap.to(next.container, {

        opacity: 1,

        duration: 0.5,

        ease: 'power2.inOut'

      });

    }

  }]

});

3. Crossfade Transition (Sync)

Simultaneous fade between pages:

barba.init({

  transitions: [{

    name: 'crossfade',

    sync: true, // Enable sync mode

    leave({ current }) {

      return gsap.to(current.container, {

        opacity: 0,

        duration: 0.8,

        ease: 'power2.inOut'

      });

    },

    enter({ next }) {

      return gsap.from(next.container, {

        opacity: 0,

        duration: 0.8,

        ease: 'power2.inOut'

      });

    }

  }]

});

4. Slide Transition with Overlap

Slide old page out, new page in with overlap:

barba.init({

  transitions: [{

    name: 'slide',

    sync: true,

    leave({ current }) {

      return gsap.to(current.container, {

        x: '-100%',

        duration: 0.7,

        ease: 'power3.inOut'

      });

    },

    enter({ next }) {

      // Start off-screen right

      gsap.set(next.container, { x: '100%' });

      // Slide in from right

      return gsap.to(next.container, {

        x: '0%',

        duration: 0.7,

        ease: 'power3.inOut'

      });

    }

  }]

});

5. Transition Rules (Conditional Transitions)

Define different transitions based on navigation context:

barba.init({

  transitions: [

    // Home to any page: fade

    {

      name: 'from-home-fade',

      from: { namespace: 'home' },

      leave({ current }) {

        return gsap.to(current.container, {

          opacity: 0,

          duration: 0.5

        });

      },

      enter({ next }) {

        return gsap.from(next.container, {

          opacity: 0,

          duration: 0.5

        });

      }

    },

    // Product to product: slide left

    {

      name: 'product-to-product',

      from: { namespace: 'product' },

      to: { namespace: 'product' },

      leave({ current }) {

        return gsap.to(current.container, {

          x: '-100%',

          duration: 0.6

        });

      },

      enter({ next }) {

        gsap.set(next.container, { x: '100%' });

        return gsap.to(next.container, {

          x: '0%',

          duration: 0.6

        });

      }

    },

    // Default fallback

    {

      name: 'default',

      leave({ current }) {

        return gsap.to(current.container, {

          opacity: 0,

          duration: 0.3

        });

      },

      enter({ next }) {

        return gsap.from(next.container, {

          opacity: 0,

          duration: 0.3

        });

      }

    }

  ]

});

6. Router Plugin for Route-Based Transitions

Use @barba/router for route-specific transitions:

Installation:

npm install --save-dev @barba/router

Usage:

import barba from '@barba/core';

import barbaPrefetch from '@barba/prefetch';

import barbaRouter from '@barba/router';

// Define routes

barbaRouter.init({

  routes: [

    { path: '/', name: 'home' },

    { path: '/about', name: 'about' },

    { path: '/products/:id', name: 'product' }, // Dynamic segment

    { path: '/blog/:category/:slug', name: 'blog-post' }

  ]

});

barba.use(barbaRouter);

barba.use(barbaPrefetch); // Optional: prefetch on hover

barba.init({

  transitions: [{

    name: 'product-transition',

    to: { route: 'product' }, // Trigger on route name

    leave({ current }) {

      return gsap.to(current.container, {

        scale: 0.95,

        opacity: 0,

        duration: 0.5

      });

    },

    enter({ next }) {

      return gsap.from(next.container, {

        scale: 1.05,

        opacity: 0,

        duration: 0.5

      });

    }

  }]

});

7. Loading Indicator

Show loading state during page fetch:

barba.init({

  transitions: [{

    async leave({ current }) {

      // Show loader

      const loader = document.querySelector('.loader');

      gsap.set(loader, { display: 'flex', opacity: 0 });

      gsap.to(loader, { opacity: 1, duration: 0.3 });

      // Fade out page

      await gsap.to(current.container, {

        opacity: 0,

        duration: 0.5

      });

    },

    async enter({ next }) {

      // Hide loader

      const loader = document.querySelector('.loader');

      await gsap.to(loader, { opacity: 0, duration: 0.3 });

      gsap.set(loader, { display: 'none' });

      // Fade in page

      await gsap.from(next.container, {

        opacity: 0,

        duration: 0.5

      });

    }

  }]

});

Integration Patterns

GSAP Integration

Barba.js works seamlessly with GSAP for animations:

Timeline-Based Transitions:

import barba from '@barba/core';

import gsap from 'gsap';

barba.init({

  transitions: [{

    async leave({ current }) {

      const tl = gsap.timeline();

      tl.to(current.container.querySelector('h1'), {

        y: -50,

        opacity: 0,

        duration: 0.3

      })

      .to(current.container.querySelector('.content'), {

        y: -30,

        opacity: 0,

        duration: 0.3

      }, '-=0.2')

      .to(current.container, {

        opacity: 0,

        duration: 0.2

      });

      await tl.play();

    },

    async enter({ next }) {

      const tl = gsap.timeline();

      // Set initial states

      gsap.set(next.container, { opacity: 0 });

      gsap.set(next.container.querySelector('h1'), { y: 50, opacity: 0 });

      gsap.set(next.container.querySelector('.content'), { y: 30, opacity: 0 });

      tl.to(next.container, {

        opacity: 1,

        duration: 0.2

      })

      .to(next.container.querySelector('h1'), {

        y: 0,

        opacity: 1,

        duration: 0.5,

        ease: 'power3.out'

      })

      .to(next.container.querySelector('.content'), {

        y: 0,

        opacity: 1,

        duration: 0.5,

        ease: 'power3.out'

      }, '-=0.3');

      await tl.play();

    }

  }]

});

Reference gsap-scrolltrigger skill for advanced GSAP integration patterns.

View-Specific Initialization

Initialize libraries or scripts per page:

barba.init({

  views: [

    {

      namespace: 'home',

      afterEnter() {

        // Initialize home page features

        initHomepageSlider();

        initParallaxEffects();

      },

      beforeLeave() {

        // Clean up

        destroyHomepageSlider();

      }

    },

    {

      namespace: 'gallery',

      afterEnter() {

        initLightbox();

        initMasonry();

      },

      beforeLeave() {

        destroyLightbox();

      }

    }

  ]

});

Analytics Tracking

Track page views on navigation:

barba.hooks.after(() => {

  // Google Analytics

  if (typeof gtag !== 'undefined') {

    gtag('config', 'GA_MEASUREMENT_ID', {

      page_path: window.location.pathname

    });

  }

  // Or use data layer

  window.dataLayer = window.dataLayer || [];

  window.dataLayer.push({

    event: 'pageview',

    page: window.location.pathname

  });

});

Third-Party Script Re-Initialization

Re-run scripts after page transitions:

barba.hooks.after(() => {

  // Re-initialize third-party widgets

  if (typeof twttr !== 'undefined') {

    twttr.widgets.load(); // Twitter widgets

  }

  if (typeof FB !== 'undefined') {

    FB.XFBML.parse(); // Facebook widgets

  }

  // Re-run syntax highlighting

  if (typeof Prism !== 'undefined') {

    Prism.highlightAll();

  }

});

Performance Optimization

1. Prefetching

Use @barba/prefetch to load pages on hover:

npm install --save-dev @barba/prefetch
import barba from '@barba/core';

import barbaPrefetch from '@barba/prefetch';

barba.use(barbaPrefetch);

barba.init({

  // Prefetch fires on link hover by default

  prefetch: {

    root: null, // Observe all links

    timeout: 3000 // Cache timeout in ms

  }

});

2. Prevent Layout Shift

Set container min-height to prevent content jump:

[data-barba="container"] {

  min-height: 100vh;

  /* Or use viewport height minus header/footer */

  min-height: calc(100vh - 80px - 60px);

}

3. Optimize Animations

Use GPU-accelerated properties:

// ✅ Good - GPU accelerated

gsap.to(element, {

  opacity: 0,

  x: -100,

  scale: 0.9,

  rotation: 45

});

// ❌ Avoid - causes reflow/repaint

gsap.to(element, {

  width: '50%',

  height: '300px',

  top: '100px'

});

4. Clean Up Event Listeners

Remove listeners in beforeLeave or view hooks:

barba.init({

  views: [{

    namespace: 'home',

    afterEnter() {

      // Add listeners

      this.clickHandler = () => console.log('clicked');

      document.querySelector('.btn').addEventListener('click', this.clickHandler);

    },

    beforeLeave() {

      // Remove listeners

      document.querySelector('.btn').removeEventListener('click', this.clickHandler);

    }

  }]

});

5. Lazy Load Images

Defer image loading until after transition:

barba.init({

  transitions: [{

    async enter({ next }) {

      // Complete transition first

      await gsap.from(next.container, {

        opacity: 0,

        duration: 0.5

      });

      // Then load images

      const images = next.container.querySelectorAll('img[data-src]');

      images.forEach(img => {

        img.src = img.dataset.src;

        img.removeAttribute('data-src');

      });

    }

  }]

});

Common Pitfalls

1. Forgetting to Return Promises

Problem: Transitions complete instantly without waiting for animations.

Solution: Always return promises or use async/await:

// ❌ Wrong - animation starts but doesn't wait

leave({ current }) {

  gsap.to(current.container, { opacity: 0, duration: 0.5 });

}

// ✅ Correct - returns promise

leave({ current }) {

  return gsap.to(current.container, { opacity: 0, duration: 0.5 });

}

// ✅ Also correct - async/await

async leave({ current }) {

  await gsap.to(current.container, { opacity: 0, duration: 0.5 });

}

2. Not Preventing Default Link Behavior

Problem: Some links cause full page reloads.

Solution: Barba automatically prevents default on internal links, but you may need to exclude external links:

barba.init({

  prevent: ({ href }) => {

    // Allow external links

    if (href.indexOf('http') > -1 &#x26;&#x26; href.indexOf(window.location.host) === -1) {

      return true;

    }

    return false;

  }

});

3. CSS Conflicts Between Pages

Problem: Old page CSS affects new page layout during transition.

Solution: Use namespace-specific CSS or reset styles:

/* Namespace-specific styles */

[data-barba-namespace="home"] .hero {

  background: blue;

}

[data-barba-namespace="about"] .hero {

  background: red;

}

Or reset in beforeEnter:

beforeEnter({ next }) {

  // Reset scroll position

  window.scrollTo(0, 0);

  // Reset any global state

  document.body.classList.remove('menu-open');

}

4. Not Updating Document Title and Meta Tags

Problem: Page title and meta tags don't update on navigation.

Solution: Use @barba/head plugin or update manually:

npm install --save-dev @barba/head
import barba from '@barba/core';

import barbaHead from '@barba/head';

barba.use(barbaHead);

barba.init({

  // Head plugin automatically updates <head> tags

});

Or manually:

barba.hooks.after(({ next }) => {

  // Update title

  document.title = next.html.querySelector('title').textContent;

  // Update meta tags

  const newMeta = next.html.querySelectorAll('meta');

  newMeta.forEach(meta => {

    const name = meta.getAttribute('name') || meta.getAttribute('property');

    if (name) {

      const existing = document.querySelector(`meta[name="${name}"], meta[property="${name}"]`);

      if (existing) {

        existing.setAttribute('content', meta.getAttribute('content'));

      }

    }

  });

});

5. Animation Flicker on Enter

Problem: New page flashes visible before enter animation starts.

Solution: Set initial invisible state in CSS or beforeEnter:

/* CSS approach */

[data-barba="container"] {

  opacity: 0;

}

[data-barba="container"].is-visible {

  opacity: 1;

}
// JavaScript approach

beforeEnter({ next }) {

  gsap.set(next.container, { opacity: 0 });

}

6. Sync Transitions Without Proper Positioning

Problem: Sync transitions cause layout shift as containers stack.

Solution: Position containers absolutely during transition:

[data-barba="wrapper"] {

  position: relative;

}

[data-barba="container"] {

  position: absolute;

  top: 0;

  left: 0;

  width: 100%;

}

Or manage in JavaScript:

barba.init({

  transitions: [{

    sync: true,

    beforeLeave({ current }) {

      gsap.set(current.container, {

        position: 'absolute',

        top: 0,

        left: 0,

        width: '100%'

      });

    }

  }]

});

Resources

This skill includes:

scripts/

Executable utilities for common Barba.js tasks:

  • transition_generator.py - Generate transition boilerplate code
  • project_setup.py - Initialize Barba.js project structure

references/

Detailed documentation:

  • api_reference.md - Complete Barba.js API (hooks, transitions, views, router)
  • hooks_guide.md - All 11 hooks with execution order and use cases
  • gsap_integration.md - GSAP animation patterns for Barba transitions
  • transition_patterns.md - Common transition implementations

assets/

Templates and starter projects:

  • starter_barba/ - Complete Barba.js + GSAP starter template
  • examples/ - Real-world transition implementations

Related Skills

  • gsap-scrolltrigger - For advanced GSAP animations in transitions
  • locomotive-scroll - Can be combined with Barba for smooth scrolling between pages
  • motion-framer - Alternative approach for React-based page transitions
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card