Transition With React Transition Group

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

Submit a Comment

Your email address will not be published. Required fields are marked *

Subscribe

Select Categories