[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
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.
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
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.
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
.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
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
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
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
- [Next.js]: Set up Sass/Scss environment with Next.js
- [Next.js]: How to set up Express.js with Next.js