In this article, we will learn how we can grouping transition effect in react. We are going to use ‘react-transition-group’ for this.
The Transition component lets you describe a transition from one component state to another. Most commonly it’s used to animate the mounting and unmounting of a component, but can also be used to describe in-place transition states as well.
If you are new to react or follow the steps to create a react app from here. I assume you have already created a react app. Now you need to follow the below steps.
At first we have to install react-router-dom for easy routing.
npm i react-router-dom --save
Install react-transition-group, to install this you need to execute the below command in the terminal.
npm i react-transition-group --save
Now We have to create 2 or 3 components or pages, Here I have created Home.js, Page.js, Subpage.js, Nav.js in src folder.
So, create Home.js file and add below code
import React, { Component } from "react"; import {Link} from 'react-router-dom' export default class Home extends Component { constructor(props){ super(props) } render() { return ( <div className={this.props.active ? "page page_open" : "page page_close"}> <div className="page-internal"> <h1>Home</h1> <p>Hello from the home page!</p> <Link to="/subpage" className="link">Click here to go to Subpage</Link> </div> </div> ) } }
Now create Page.js file and add below code
import React, { Component } from "react"; export default class Page extends Component { constructor(props) { super(props) } render() { return ( <div className={this.props.active ? "page page_open" : "page page_close"}> <div className="page-internal"> <h1>Page</h1> <p>Hello from Page!</p> </div> </div> ) } }
Now create Subpage.js file and add below code
import React, { Component } from "react"; import {Link} from 'react-router-dom' export default class Subpage extends Component { constructor(props){ super(props) } render() { return ( <div className={this.props.active ? "page page_open" : "page page_close"}> <div className="page-internal"> <h1>Subpage</h1> <p>Hello from a sub page!</p> <Link to="/" className="link">Click here to go to Home</Link> </div> </div> ) } }
And for Nav menu I have created Nav.js file, add below code in that
import React from 'react' import { Link } from 'react-router-dom' export default class Nav extends React.Component { render() { return ( <div className={this.props.active ? "pages-nav pages-nav_open" : "pages-nav"}> <div className="pages-nav__item"> <Link to="/" className="link link_page">Home</Link> </div> <div className="pages-nav__item"> <Link to="/page" className="link link_page">Page</Link> </div> <div className="pages-nav__item"> <Link to="/subpage" className="link link_page">Subpage</Link> </div> </div> ) } }
For some styling add below code in App.css file
html, body, #root {overflow: hidden;height: 100vh;} body { margin: 0; padding: 0; font-family: Helvetica, Arial, sans-serif; color: #cecece; background: #1d1e21; } .App {position: relative;} a{text-decoration: none;} /* fade transition effect */ .fade-enter{ transform: translate3d(0px, 75%, -150px) !important; z-index: 8; opacity: 0.9; } .fade-enter.fade-enter-active { opacity: 1; z-index: 8; transform: translate3d(0px, 0px, 0px) !important; -webkit-transition: -webkit-transform 0.45s, opacity 0.45s; transition: transform 0.45s, opacity 0.45s !important; -webkit-transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1); transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1) !important; } .fade-exit { position: absolute !important; top: 0px; left: 0; opacity: 1; } .fade-exit.fade-exit-active { opacity: 0.9; transform: translate3d(0px, 100%, 0px); -webkit-transition: -webkit-transform 0.45s, opacity 0.45s !important; transition: transform 0.45s, opacity 0.45s !important; -webkit-transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1); transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1) !important; } /* Menu button */ .menu-button { position: absolute; z-index: 1000; top: 30px; left: 30px; width: 30px; height: 24px; padding: 0; cursor: pointer; border: none; outline: none; background: transparent; } .no-js .menu-button {display: none;} .menu-button::before, .menu-button::after, .menu-button span {background: #5f656f;} .menu-button::before,.menu-button::after { content: ''; position: absolute; top: 50%; left: 0; width: 100%; height: 2px; pointer-events: none; -webkit-transition: -webkit-transform 0.25s; transition: transform 0.25s; -webkit-transform-origin: 50% 50%; transform-origin: 50% 50%; } .menu-button span { position: absolute; left: 0; overflow: hidden; width: 100%; height: 2px; text-indent: 200%; -webkit-transition: opacity 0.25s; transition: opacity 0.25s; } .menu-button::before {-webkit-transform: translate3d(0, -10px, 0) scale3d(0.8, 1, 1); transform: translate3d(0, -10px, 0) scale3d(0.8, 1, 1);} .menu-button::after { -webkit-transform: translate3d(0, 10px, 0) scale3d(0.8, 1, 1);transform: translate3d(0, 10px, 0) scale3d(0.8, 1, 1);} .menu-button_open span {opacity: 0;} .menu-button_open::before { -webkit-transform: rotate3d(0, 0, 1, 45deg); transform: rotate3d(0, 0, 1, 45deg);} .menu-button_open::after {-webkit-transform: rotate3d(0, 0, 1, -45deg); transform: rotate3d(0, 0, 1, -45deg);} /* Pages stack */ .pages-stack { z-index: 100; pointer-events: none; -webkit-perspective: 1200px; perspective: 1200px; -webkit-perspective-origin: 50% -50%; perspective-origin: 50% -50%; } .page { position: relative; z-index: 5; overflow: hidden; width: 100%; height: 100vh; pointer-events: auto; background: #2a2a2a; box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1); } .pages-stack_open .page { cursor: pointer; -webkit-transition: -webkit-transform 0.45s, opacity 0.45s; transition: transform 0.45s, opacity 0.45s; -webkit-transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1); transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1); } .pages-stack .page { cursor: pointer; -webkit-transition: -webkit-transform 0.45s, opacity 0.45s; transition: transform 0.45s, opacity 0.45s; -webkit-transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1); transition-timing-function: cubic-bezier(0.6, 0, 0.4, 1); } .page-inactive { position: absolute; z-index: 0; top: 0; opacity: 0; } .page-inactive2 { position: absolute; z-index: 0; top: 0; opacity: 0; } .page_open{ z-index: 9; opacity: 1; transform: translate3d(0px, 30%, -220px); } .page_close{ z-index: 9; opacity: 1; transform: translate3d(0px, 0px, 0px); } .page-inactive_open{ transform: translate3d(0px, 30%, -150px); z-index: 8; opacity: 0.9; } .page-inactive_close{ transform: translate3d(0px, 100%, 0px); z-index: 8; opacity: 0.9; } .page-inactive2_open{ transform: translate3d(0px, 30%, -300px); z-index: 7; opacity: 0.8; } .page-inactive2_close{ transform: translate3d(0px, 100%, 0px); z-index: 7; opacity: 0.8; } .page-internal{ padding:70px;} .pages-nav { display: -webkit-flex; display: flex; -webkit-flex-wrap: wrap; flex-wrap: wrap; -webkit-justify-content: center; justify-content: space-around; -webkit-align-itrems: center; align-items: center; text-align: center; background: #0e0f0f; position: absolute; top: 0; left: 0; box-sizing: border-box; width: 100%; padding: 30px; pointer-events: none; opacity: 0; background: transparent; -webkit-transition: -webkit-transform 1.2s, opacity 1.2s; transition: transform 1.2s, opacity 1.2s; -webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1); transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1); -webkit-transform: translate3d(0, 150px, 0); transform: translate3d(0, 150px, 0); } .pages-nav_open { pointer-events: auto; opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .pages-nav_item { width: 33%; padding: 1rem; box-sizing: border-box; } .pages-nav_item { padding: 0 10%; } .link { font-size: 0.85rem; font-weight: bold; position: relative; letter-spacing: 1px; text-transform: uppercase; color: #fff; } .link:hover,.link:focus { color: #fff;} .link_page {display: block; color: #cecece;} .link_page:not(.link_faded)::before { content: ''; position: absolute; top: 100%; left: 50%; width: 30px; height: 2px; margin: 5px 0 0 -15px; background: #fff; -webkit-transition: -webkit-transform 0.3s; transition: transform 0.3s; -webkit-transform: scale3d(0, 1, 1); transform: scale3d(0, 1, 1); } .link_page:hover:before {-webkit-transform: scale3d(1, 1, 1); transform: scale3d(1, 1, 1);} .link_faded { color: #444;} .link_faded:hover,.link_faded:focus { color: #5c5edc;} .link_page.link_faded { font-size: 0.65rem;} .link--social { font-size: 1.5rem; margin: 0 0.75rem;} .text-hidden { position: absolute; display: block; overflow: hidden; width: 0; height: 0; color: transparent; } @media screen and (max-width: 60rem) { .pages-nav_item { width: 50%; min-height: 20px; } .link_page { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .link{ font-size: 1rem; } .link_page.link_faded { font-size: 0.85rem; } } @media screen and (max-width: 40rem) { .pages-nav { display: block; text-align: left; } .pages-nav_item { width: 100%; padding: 4px 0; } .menu-button { top: 15px; right: 10px; left: auto; } }
At last add below code in App.js file for transition routing.
import React, { Component } from 'react'; import { Route, BrowserRouter as Router, Switch } from "react-router-dom"; import { TransitionGroup, CSSTransition } from "react-transition-group"; import Home from "./Home"; import Subpage from "./Subpage"; import Nav from './Nav'; import Page from './Page'; import './App.css'; class App extends Component { constructor() { super(); this.state = { active: false }; } menuClicked = () => { this.setState({ active: !this.state.active }) } render() { return ( <Router> <div className="App"> <button className={this.state.active ? "menu-button menu-button_open" : "menu-button"} onClick={this.menuClicked}> <span>Menu</span> </button> <Nav active={this.state.active} /> <div className={this.state.active ? "pages-stack pages-stack_open" : "pages-stack"}> <div className={this.state.active ? "page page-inactive page-inactive_open" : "page page-inactive page-inactive_close"}></div> <div className={this.state.active ? "page page-inactive2 page-inactive2_open" : "page page-inactive2 page-inactive2_close"}></div> <div> <Route render={({ location }) => { const { pathname } = location; console.log(pathname); return ( <TransitionGroup> <CSSTransition key={pathname} classNames="fade" timeout={{ enter: 300, exit: 300, }} > <Route location={location} render={() => ( <Switch> <Route exact path="/" render={props => ( <Home {...props} active={this.state.active} /> )} /> <Route exact path="/subpage" render={props => ( <Subpage {...props} active={this.state.active} /> )} /> <Route path="/page" render={props => ( <Page {...props} active={this.state.active} /> )} /> </Switch> )} /> </CSSTransition> </TransitionGroup> ); }} /> </div> </div> </div> </Router> ); } } export default App;
That’s it! Now check your Output