April 8, 2026/Simple/Page Transitions

Slide Up Transition

Page elements slide up from below and fade in on enter. Exit is a shared opacity fade. gsap.fromTo targets all page elements with a 0.1s stagger creating a sequential cascade.

Home

  • Featured Work
  • Latest Projects
  • About Studio
"use client";

import { useEffect, useRef, useState } from "react";
import gsap from "gsap";
import styles from "./styles.module.css";

const PAGES = [
  { label: "Home", bg: "#f5f5f5", color: "#1a1a1a", items: ["Featured Work", "Latest Projects", "About Studio"] },
  { label: "About", bg: "#0d0d0d", color: "#ffffff", items: ["Our Story", "Team", "Values"] },
  { label: "Work", bg: "#1a1f2e", color: "#ffffff", items: ["Brand Identity", "Web Design", "Motion"] },
];

export default function SlideUpTransition() {
  const [pageIndex, setPageIndex] = useState(0);
  const [displayIndex, setDisplayIndex] = useState(0);
  const containerRef = useRef(null);
  const isAnimating = useRef(false);

  function navigate(nextIndex) {
    if (isAnimating.current || nextIndex === pageIndex) return;
    isAnimating.current = true;

    const container = containerRef.current;
    // Exit: fade out current page
    gsap.to(container, {
      opacity: 0,
      duration: 0.3,
      ease: "power1.in",
      onComplete: () => {
        setDisplayIndex(nextIndex);
        setPageIndex(nextIndex);
      },
    });
  }

  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const items = container.querySelectorAll(`.${styles.item}`);
    if (!items.length) return;

    // Enter: slide up from below
    gsap.fromTo(
      items,
      { opacity: 0, y: 30 },
      {
        opacity: 1,
        y: 0,
        stagger: 0.1,
        duration: 0.5,
        ease: "power2.out",
        onStart: () => gsap.set(container, { opacity: 1 }),
        onComplete: () => { isAnimating.current = false; },
      }
    );
  }, [displayIndex]);

  const page = PAGES[displayIndex];

  return (
    <div className={styles.demo}>
      <div
        ref={containerRef}
        className={styles.page}
        style={{ background: page.bg, color: page.color }}
      >
        <h2 className={styles.pageTitle}>{page.label}</h2>
        <ul className={styles.list}>
          {page.items.map((item) => (
            <li key={item} className={styles.item}>{item}</li>
          ))}
        </ul>
      </div>
      <div className={styles.nav}>
        {PAGES.map((p, i) => (
          <button
            key={p.label}
            className={`${styles.navBtn} ${i === pageIndex ? styles.active : ""}`}
            onClick={() => navigate(i)}
          >
            {p.label}
          </button>
        ))}
      </div>
    </div>
  );
}
  • gsap

Related Components

1W AGO
Signifly Theme Fadepage-transitions
3MO AGO
Section Transition Systempage-transitions
3MO AGO
Shutter Sectionpage-transitions