• Stars
    star
    185
  • Rank 208,271 (Top 5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 3 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Build complex animated page transitions with GSAP and Next.js

Overview

TweenPages is on Github: github.com/johnpolacek/TweenPages

Why Next.js?

One of the things that makes Next.js so great is that you can create a modern, slick single page app experience while keeping the performance and other benefits of classic server-side rendering and routing.

There is an official next-page-transitions example that demonstrates how to add a loading state when navigating to a page, wait for the page to load its content and animate in when it's ready.

What about something more ambitious and complex? We could offer something more ambitious:

  • Intro and outro animations defined at the component level
  • A higher order component that manages page transitions and the animations of nested child components
  • Re-usable, declarative animation componenents
  • No flash of unstyled content on initial page load

Why GSAP?

Back in the early days of the web, if you wanted to craft complex, ambitious animations as part of a web experience, you would use Flash. Thanks to advancements in the web platform and open source project, we can build these today, but in an accessible, responsive, performant way.

The Greensock Animation Platform was the best animation library in Flash and is today the most robust animation library for the web, benefiting from many years of enhancements and platform stability.

A great place to get started with GSAP and React is to read GSAP + React, First Steps & Handy Techniques and GSAP + React, Advanced Animation Techniques.

Intros and Outros

The Intro/Outro Pattern

A useful pattern in building animations into applications is the concept of component-level intro/outro animations, something that GSAP is very well suited for.

Each component can have its own animation as it gets added to the view, an intro. Typically you would do this by defining styles for the starting values for an element, such as opacity:0 for a fade in. Then setting another style for the ending value when you element comes to rest (e.g. opacity:1).

Your components may be composed of multiple elements, with different values that get transitioned, perhaps flying in, rotating, scaling, etc.

The outro is typically the intro animation in reverse, where you set a value that you want the element to transition to before it is removed from the view (e.g. opacity:1.

It is of course possible you may wish to mix and match these intro/outro animations, for example a fly in from the bottom of the view, then a static fade out for the exit.

Intro Animations in Next.JS

For an intro animations in React, you might typically reach for useLayoutEffect, but because Next.js does SSR, your console will fill up with warnings for every animated component you have on your page. To avoid this we can apply useIsomorphicLayoutEffect instead of useEffect. Check it out on Github.

Additionally with SSR, to prevent the flash of unstyled content (FOUC). This frequently happens when an element on a server-side rendered page displays for a brief moment while the JavaScript is loaded and executed.

For example, if you have a fade intro on your element, there will be a brief flash where it is displayed at full opacity, then it will disappear and fade in as intended.

To avoid this, we need to make sure the initial styling state of the component is correct. For example, if we are fading in, the initial style of that component should be an opacity of zero.

Outro Animations in Next.JS

Outro animations are where it gets tricky. We need to intercept the page transition, and do whatever exit animations our child components need to do, then proceed to go to the next page, where our components will all animate in.

To pull this off, we will make use of the following:

  • A TransitionLayout higher order component used as a wrapper in MyApp that will delay the routing change until after any animations have completed.
  • A TimelineProvider component that will take advantage of React’s useContext hook to share an outro timeline across multiple components, wherever they are nested in our app

Page Transitions

In Next.js, we can add a custom App component to initialize pages. It is typical to use this to persist layouts between page changes (e.g. a header navigation bar).

Additionally, we can use the Custom App component to manage our page transition animations.

Transition Provider

In order to make a page transition effect, we need to prevent rendering the new page before our outro animation is done.

We may have many components with different animation effects nested in our pages. To keep track of all the different outro transitions, we will use a combination of React’s Context API and a top-level GSAP timeline.

In TransitionContext we will create our TransitionProvider which will make our GSAP timeline for outro animations available to any components who would like to transition out during a page change.

import React, { useState, createContext, useCallback } from "react"
import gsap from "gsap"

const TransitionContext = createContext({})

const TransitionProvider = ({ children }) => {
  const [timeline, setTimeline] = useState(() =>
    gsap.timeline({ paused: true })
  )

  return (
    <TransitionContext.Provider
      value={{
        timeline,
        setTimeline,
      }}
    >
      {children}
    </TransitionContext.Provider>
  )
}

export { TransitionContext, TransitionProvider }

What if you have pages with different background colors and you would like to transition smoothly from one to another? We can can add background as another property in addition to timeline in TransitionContext.

Transition Layout

Next, we have TransitionLayout which will be our controller that will initiate the outro animations and update the page when they are all complete. It also contains a wrapper component for the background color page transition animation.

import { gsap } from "gsap"
import { TransitionContext } from "../context/TransitionContext"
import { useState, useContext, useRef } from "react"
import useIsomorphicLayoutEffect from "../animation/useIsomorphicLayoutEffect"

export default function TransitionLayout({ children }) {
  const [displayChildren, setDisplayChildren] = useState(children)
  const { timeline, background } = useContext(TransitionContext)
  const el = useRef()

  useIsomorphicLayoutEffect(() => {
    if (children !== displayChildren) {
      if (timeline.duration() === 0) {
        // there are no outro animations, so immediately transition
        setDisplayChildren(children)
      } else {
        timeline.play().then(() => {
          // outro complete so reset to an empty paused timeline
          timeline.seek(0).pause().clear()
          setDisplayChildren(children)
        })
      }
    }
  }, [children])

  useIsomorphicLayoutEffect(() => {
    gsap.to(el.current, {
      background,
      duration: 1,
    })
  }, [background])

  return <div ref={el}>{displayChildren}</div>
}

Custom App

Let’s take a look at our Custom App component.

import { TransitionProvider } from "../src/context/TransitionContext"
import TransitionLayout from "../src/animation/TransitionLayout"
import { Box } from "theme-ui"
import Header from "../src/ui/Header"
import Footer from "../src/ui/Footer"

export default function MyApp({ Component, pageProps }) {
  return (
    <TransitionProvider>
      <TransitionLayout>
        <Box
          sx={{
            display: "flex",
            minHeight: "100vh",
            flexDirection: "column",
          }}
        >
          <Header />
          <Component {...pageProps} />
          <Footer />
        </Box>
      </TransitionLayout>
    </TransitionProvider>
  )
}

Here we have TransitionProvider and TransitionLayout wrapping the other elements so that they can access our TransitionContext properties. We have a Header and Footer that exist outside of Component so that they will be static after the initial page load.

Component-Level Animation

Here is an example of a basic animation we can do at the component level. We can add as many of these as we want to a page and they will all do the same thing, wrap all its children in a transparent div and fade it in on page load, then fade out when navigating to a different page.

import { useRef, useContext } from "react"
import { gsap } from "gsap"
import { Box } from "theme-ui"
import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect"
import { TransitionContext } from "../context/TransitionContext"

const FadeInOut = ({ children }) => (
  const { timeline } = useContext(TransitionContext)
  const el = useRef()

  // useIsomorphicLayoutEffect to avoid console warnings
  useIsomorphicLayoutEffect(() => {
    // intro animation will play immediately
    gsap.to(el.current, {
      opacity: 1,
      duration: 1,
    })

    // add outro animation to top-level outro animation timeline
    timeline.add(
      gsap.to(el.current, {
        opacity: 0,
        duration: .5,
      }),
      0
    )
  }, [])

  // set initial opacity to 0 to avoid FOUC for SSR
  <Box ref={el} sx={{opacity: 0}}>
    {children}
  </Box>
)

export default FadeInOut

We can take this pattern and extract it into an extendable AnimateInOut helper component for reusable intro/outro animation patterns in our app.

import React, { useRef, useContext } from "react"
import { gsap } from "gsap"
import { Box } from "theme-ui"
import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect"
import { TransitionContext } from "../context/TransitionContext"

const AnimateInOut = ({
  children,
  as,
  from,
  to,
  durationIn,
  durationOut,
  delay,
  delayOut,
  set,
  skipOutro,
}) => {
  const { timeline } = useContext(TransitionContext)
  const el = useRef()

  useIsomorphicLayoutEffect(() => {
    // intro animation
    if (set) {
      gsap.set(el.current, { ...set })
    }
    gsap.to(el.current, {
      ...to,
      delay: delay || 0,
      duration: durationIn,
    })

    // outro animation
    if (!skipOutro) {
      timeline.add(
        gsap.to(el.current, {
          ...from,
          delay: delayOut || 0,
          duration: durationOut,
        }),
        0
      )
    }
  }, [])

  return (
    <Box as={as} sx={from} ref={el}>
      {children}
    </Box>
  )
}

export default React.memo(AnimateInOut)

The AnimateInOut component has built in flexibility for different scenarios:

  • Setting different animations, durations and delays for intros and outros
  • Skipping the outro
  • Setting the element tag for the wrapper, e.g. use a <span> instead of a <div>
  • Use GSAP’s set option to define initial values for the intro

Using this we can create all sorts of reusable intro/outro animations, such as <FlyInOut>, <ScaleInOut>, <RotateInOut3D> and so forth.

More Repositories

1

scrollorama

The jQuery plugin for doing cool scrolly stuff. NOTE: No longer under active development. New version is ScrollMagic.js
JavaScript
2,510
star
2

superscrollorama

The original jQuery plugin for supercool scroll animation. NOTE: No longer under active development. New version is ScrollMagic.js
JavaScript
2,394
star
3

BigVideo.js

The jQuery Plugin for Big Background Video (and Images)
JavaScript
2,254
star
4

stacktable.js

jQuery plugin for stacking tables on small screens
HTML
1,026
star
5

scrolldeck.js

jQuery plugin for making scrolling presentation decks
JavaScript
673
star
6

imagefill.js

The jQuery plugin for making images fill their containers (and be centered)
HTML
609
star
7

date-range-picker-for-shadcn

DateRangePicker is a reusable component for shadcn using Radix UI and Tailwind CSS. Includes preset date ranges, text entry, calendar selection and date comparison
TypeScript
600
star
8

controldeck.js

control html5 presentations with node.js
JavaScript
357
star
9

Responsivator

Website for viewing and sharing how web pages look at different screen sizes
JavaScript
317
star
10

expressive-css

An approach to writing lightweight, scalable CSS using utility classes that are easy to write and understand.
HTML
279
star
11

extra-strength-responsive-grids

A Fluid CSS Grid System for Responsive Web Design The Fluid CSS Grid System for Responsive Web Design. Take total control of your layouts.
CSS
252
star
12

nextjs-mdx-blog-starter

Next.js MDX Blog Starter for building blogs with Next.js and MDX, including Theme UI Component Design System, Vercel Deployment and more.
JavaScript
172
star
13

design-system-playground

Play with typography and colors to generate a design system theme you can use in your projects.
JavaScript
160
star
14

ResponsiveThumbnailGallery

jQuery Plugin for creating image galleries that scale to fit their container.
JavaScript
95
star
15

styled-starter

Starter Kit with React, Next.js, Styled System and Catalog to get projects going with a design theme, built-in styleguide and browser testing
JavaScript
80
star
16

animated-gradient-background-generator

Animated CSS Gradient Background Generator
JavaScript
73
star
17

tweendeck

Next level animation for web presentations
HTML
66
star
18

MagicNav.js

The jQuery Plugin for generating nav links from page elements
JavaScript
63
star
19

styled-system-html

HTML elements extended as styled components via styled system, ready for theming.
JavaScript
62
star
20

serverless-cms

Proof of concept demo for creating a custom serverless CMS for generating static websites
JavaScript
50
star
21

nextjs-scraper-playground

Build and test your own web scraper APIs with Next.js API Routes and cheerio
JavaScript
45
star
22

the-case-for-atomic-css

A collection of various content detailing and demonstrating the benefits of Atomic CSS, also known as Utility First or Functional CSS.
HTML
42
star
23

SimpleVid

jQuery Plugin for Fluid Video
ActionScript
40
star
24

react-widget-library-starter

React boilerplate for producing libraries of embeddable widgets
JavaScript
37
star
25

Match-The-Letter-Game

Match The Letter is a cross-device mobile educational game for kids built with the Corona SDK
Lua
35
star
26

sizeit.js

sizeit.js is a JavaScript utility that detects the screen size and loads external css based on the settings you configure. Works kinda like media queries.
JavaScript
34
star
27

open-source-for-fame-and-fortune

Open Source For Fame and Fortune Slide Deck
JavaScript
33
star
28

free-summer

A game by Jack and John Polacek
JavaScript
30
star
29

Video-Gallery

A template for a video gallery app built in the Corona SDK
Lua
28
star
30

WhatTheHeckIsResponsiveWebDesign-impressjs

An impress.js presentation on responsive web design.
JavaScript
24
star
31

Simple-Single-Page-App

Recipe for building a single page app
JavaScript
22
star
32

simple-grid-generator

A SASS grid generator for creating responsive grids with some nice features.
CSS
22
star
33

css-in-js-or-css-and-js

This is a thing built with old fashioned CSS and JS and another same thing built with new fangled CSS-in-JS. These are both fine.
JavaScript
16
star
34

next-project-starter

Get your next React project up and running quickly with Next.js, Theme UI, MDX and more.
JavaScript
16
star
35

notyetnews

Satirical news site from the future. Generates its own articles every day with AI.
TypeScript
14
star
36

simple-grid

A simple, easy-to-use 12-column responsive grid with some nice features.
CSS
14
star
37

html5project

Template for HTML5 Websites
CSS
14
star
38

serverless-cms-2

Proof of concept demo for creating a custom serverless CMS for generating static websites
JavaScript
12
star
39

learning-node

Collection of tutorials I've taken to learn node.js
JavaScript
9
star
40

jquery.configurator

The jQuery plugin for injecting url parameter options into webpages and JSRender templates
JavaScript
9
star
41

mdx-deck-demo-greensock

MDX Deck + Greensock Animation Platform
JavaScript
9
star
42

team-health-checker

Help your team improve by taking a Team Health Check
JavaScript
8
star
43

background-pattern-overlay

Demo showing how to make a transparent background pattern on top of background colors.
7
star
44

responsive-utilities-generator

A SASS generator for creating responsive utility classes.
CSS
6
star
45

animate-in

React UI Component for animating elements in with Tailwind and CSS Animation (...and plays nice with shadcn)
TypeScript
6
star
46

jquery.framer

The jQuery Plugin for adding configurations to responsive design test pages
JavaScript
5
star
47

expressive-css-starter-kit

Get your project up and running quickly on a foundation of Expressive CSS.
HTML
5
star
48

cardtable

A responsive table component with CSS Grid, React and Tailwind CSS
JavaScript
5
star
49

chicagotechevents.com

Website for chicagotechevents.com - The best web, tech and startup events in Chicago
JavaScript
5
star
50

image-crossfade

Image Crossfade Component for Next.js Image
TypeScript
5
star
51

bucket-cms

Bucket CMS is the world’s first headless drop-in CMS, no database necessary.
TypeScript
4
star
52

code.johnpolacek.com

My open source ActionScript3 code library.
ActionScript
4
star
53

styled-starter-basic

Basic Project Setup for Styled Starter, built with React, Next.js, Styled System and Catalog to get projects going with a design theme, built-in styleguide and browser testing
JavaScript
4
star
54

jquery-databinding

Some initial thoughts on a simple way to use jQuery event handling inside objects for data binding, Observer, Pub/Sub stuff.
CSS
4
star
55

growth-areas-for-a-software-developer

Examples of how to improve as a software developer over 5 different areas: Understanding, Planning, Collaborating, Writing and Developing
JavaScript
4
star
56

bootstrap-project

JavaScript
3
star
57

covidcost

Tracking the cost of COVID-19 across China, Europe and the United States in human lives and economies.
JavaScript
3
star
58

atomic-css-from-json

Experimental Work-In-Progress: Generate static libraries of immutable utility classes from a JSON settings file
JavaScript
3
star
59

gatsby-starter-events-list

A Gatsby starter for creating a calendar list of events
JavaScript
3
star
60

trick-or-treat-it-forward

Print a QR Code to put on a sign in front of your house so trick or treaters can click a button to pledge a donation to the charity of your choice
JavaScript
3
star
61

johnpolacek.com

johnpolacek.com - built with Gatsby, Styled-Components and Styled-System
JavaScript
2
star
62

simple-flexgrid

A SASS flexbox-based grid generator for creating responsive grids with some nice features.
CSS
2
star
63

custom-rebass

A tiny example of a way to customize Rebass
JavaScript
2
star
64

johnpolacek.github.com

John Polacek on Github
CSS
2
star
65

create-bucket-cms

CLI Tool for initializing Bucket CMS
JavaScript
1
star
66

stacktable

Deprecated - see CardTable
HTML
1
star
67

team-health-checker-next

JavaScript
1
star
68

botluck

Group pot luck recipes generated by AI
TypeScript
1
star
69

expressive-css-content-display-patterns

PHP
1
star
70

botlywood

Hollywood movies generated by robots
TypeScript
1
star
71

next-project-starter-with-typescript

Get your next React project up and running quickly with Next.js, TypeScript, Theme UI, MDX and more.
TypeScript
1
star
72

The-Never-Ending-Quest-To-Build-Cool-Stuff

1
star
73

hello-next-app

Web App Project Template with Next.js, Firebase, Tailwind, Playwright and more
TypeScript
1
star
74

styled-starter-with-refunk

Basic Project Setup for Styled Starter with Refunk included for state management.
JavaScript
1
star
75

functionaizer

Create your own custom API that streams structured data from ChatGPT
TypeScript
1
star
76

nextjs-project-starters

Getting your next project up and running with these project starters for Next.js, the React Framework for Production.
JavaScript
1
star