[Next.js]: Build page transitions with React Transition Group in Next.js

Next.js

05/11/2020


Overview

In this post I'll show how to apply page transitions (a.k.a. route transitions or page animations) in Next.js with React Transition Group. I'll be using Layout component to target specific component to apply the animation.

Here is GitHub repo

Install react-transition-group

BASH
npm i react-transition-group

Layout

Start by creating Layout component. Header is a tag that won't animate, but later transition will be applied to main container.

/components/Layout/index.js
JSX
import React from "react"
import "./styles.scss"
const Layout = ({ children }) => {
return (
<div className="layout">
{/* Header component that doesn't animate */}
<header>
<h1>Header</h1>
</header>
<main className="main">{children}</main>
</div>
)
}
export default Layout

Transition

This transition will fade out to the left and fade in from the right with the changes in opacity and translateX() values.

This component needs location prop to be used as key property, so it can keep track of which component is coming in and out. location will be passed down from Layout component that will use useRoute function provided by Next.js

/components/Transition/index.js
JSX
import {
TransitionGroup,
Transition as ReactTransition,
} from "react-transition-group"
const TIMEOUT = 200
const getTransitionStyles = {
entering: {
position: `absolute`,
opacity: 0,
transform: `translateX(50px)`,
},
entered: {
transition: `opacity ${TIMEOUT}ms ease-in-out, transform ${TIMEOUT}ms ease-in-out`,
opacity: 1,
transform: `translateX(0px)`,
},
exiting: {
transition: `opacity ${TIMEOUT}ms ease-in-out, transform ${TIMEOUT}ms ease-in-out`,
opacity: 0,
transform: `translateX(-50px)`,
},
}
const Transition = ({ children, location }) => {
return (
<TransitionGroup style={{ position: "relative" }}>
<ReactTransition
key={location}
timeout={{
enter: TIMEOUT,
exit: TIMEOUT,
}}
>
{status => (
<div
style={{
...getTransitionStyles[status],
}}
>
{children}
</div>
)}
</ReactTransition>
</TransitionGroup>
)
}
export default Transition

Layout

With Transition component created, wrap the main container and pass down location with its pathname.

/components/Layout/index.js
JSX
import React from "react"
import { useRouter } from "next/router"
import Transition from "../Transition"
import "./styles.scss"
const Layout = ({ children }) => {
const router = useRouter()
return (
<div className="layout">
{/* Header component that doesn't animate */}
<header>
<h1>Header</h1>
</header>
<Transition location={router.pathname}>
<main className="main">{children}</main>
</Transition>
</div>
)
}
export default Layout

Layout Styling

/components/Layout/styles.scss
SCSS
.layout {
display: flex;
flex-direction: column;
justify-content: center;
max-width: 680px;
margin: auto;
padding: 1rem;
background: #eaeaea;
overflow: hidden;
.main {
padding: 1rem;
border-radius: 10px;
background: #ddd;
}
}

Pages

In order to navigate to different pages, create HomePage and AboutPage as an example

/pages/index.js
JSX
import React from "react"
import Link from "next/link"
const HomePage = () => {
return (
<div>
<h1>HomePage</h1>
<Link href="about">
<a>Link To About</a>
</Link>
</div>
)
}
export default HomePage
/pages/about.js
JSX
import React from "react"
import Link from "next/link"
const AboutPage = () => {
return (
<div>
<h1>AboutPage</h1>
<Link href="/">
<a>Link To Home</a>
</Link>
</div>
)
}
export default AboutPage

_app.js

To apply Layout across different page, you need to wrap the Component (pages) inside _app.js

/pages/_app.js
JSX
import Layout from "../components/Layout"
const App = ({ Component, pageProps, router }) => {
return (
<Layout>
<Component {...pageProps} key={router.route} />
</Layout>
)
}
export default App

That's it!

After making changes to _app.js, be sure to restart the server for it to take effect


WRITTEN BY

Keeping a record