One of the most frequently overlooked principles of creating interactive UIs is transitions. Fortunately, over the last few years, React.js and other component-focused frameworks have changed the way we think about UIs and how we build them.
React Transition Group allows us to transition these components in and out of the DOM in a declarative and efficient way. In this article, weāll concentrate on the CSSTransition
and TransitionGroup
components of React Transition Group using simple examples.
Prerequisites
Before we go any further, this article assumes the following:
- Node.js ā„v6 is installed on your machine
- npm is installed on your machine
- You have a basic understanding of React.js
Getting started
To install, run these commands in the terminal:
# npm npm install react-transition-group --save # yarn yarn add react-transition-group
Transition a React component using CSS with CSSTransition
The CSSTransition
component allows us to apply transitions to elements entering and leaving the DOM. We can achieve this by using the following props:
in
: a Boolean value used to control the appearance of the elementtimeout
: indicates the number of milliseconds it will take to enter or leave the DOMunmountOnExit
: indicates that when the element disappears, itās actually going to leave the DOM completely. Below is a code snippet of what it looks like:
<CSSTransition in={this.state.example} timeout={500} classNames='example' unmountOnExit >
Simple listĀ example
Below is our first example on how to use React Transition Groupās CSSTransition
:
Before CSSTransition
CSSTransition component (before transition)
CSSTransition component (before transition) by Ekwuno using classnames, react, react-dom, react-scripts, react-transition-group
After CSSTransition
CSSTransition component (with transition)
CSSTransition component (with transition) by Ekwuno using classnames, react, react-dom, react-scripts, react-transition-group
How the transition was created using CSSTransition
In the first code example, we had a normal component with no transition. This rendered an ordered list as soon as the list button was clicked, with no delay and no extra CSS styling.
But when we decide to give a little more life to this example, we install the react-transition-group
while using the <CSSTransition>
tag, and pass the information in this.state.showList
as props to in
, which enables us to add some transitions using CSS.
The timeout props allows us to apply a transition as the list leaves the DOM. We then head over to style.css
to add some styles for the transition. CSSTransition
gives us four key classNames
to use for elements entering and leaving: enter
, enter-active
, exit
, and exit-active
.
/* This fires as soon as the element enters the DOM */ .list-transition-enter{ } /* This is where we can add the transition*/ .list-transition-enter-active{ } /* This fires as soon as the this.state.showList is false */ .list-transition-exit{ } /* fires as element leaves the DOM*/ .list-transition-exit-active{ }
Then, in these CSS classes, we can add some awesome (yet simple) CSS in the classes to make it look like the child component grows out of the button.
/* This fires as soon as the element enters the DOM*/ .list-transition-enter { /*We give the list the initial dimension of the list button*/ top: 0; width: 120px; max-height: 40px; color: transparent; background-color: #5a564c; } /* This is where we can add the transition*/ .list-transition-enter-active { top: 45px; width: 200px; max-height: 200px; background-color: #9e8949; transition: all 400ms; } /* This fires as soon as the this.state.showList is false */ .list-transition-exit { top: 45px; width: 200px; max-height: 200px; background-color: #9e8949; } /* fires as element leaves the DOM*/ .list-transition-exit-active { top: 0; width: 120px; max-height: 40px; color: transparent; background-color: #5a564c; transition: all 400ms; }
Note that in the code demo above, you will notice list-transition-enter
and list-transition-exit-active
have the same values because they are the starting and ending states of the components. However, the transitions only occur when the className
is active.
Adding the appear prop to display transition onĀ load
The initial state of the list is set to false. But what if we wanted it to display as the page is mounted to the DOM? We can achieve this by just changing the state of showList
to true, but then the transition does not display using the appear
prop as shown below:
<CSSTransition in={this.state.showList} timeout={400} classNames="list-transition" unmountOnExit appear >
In the CSS file, the classNames
styling forĀ .list-transition-appear
would be the same asĀ .list-transition-enter
andĀ .list-transition-exit-active
since it occurs when the component is mounted, and its only function is to allow the transition to show as it appears.
.list-transition-enter, .list-transition-appear { /*We give the list the initial dimension of the list button*/ top: 0; width: 120px; max-height: 40px; color: transparent; background-color: #5a564c; } /* This is where we can add the transition*/ .list-transition-enter-active, .list-transition-appear-active { top: 45px; width: 200px; max-height: 200px; background-color: #9e8949; transition: all 400ms; }
Using enter and exitĀ props
Sometimes, if the application requires the transition to be disabled in some part of the componentās transition lifecycle, we can do this in the component without editing the CSS or disabling the classNames
. We do this using the enter
and exit
props like so:
<CSSTransition in={this.state.showList} timeout={400} classNames="list-transition" unmountOnExit appear enter = {false} >
This stops theĀ .list-transition-active
andĀ .list-transition-enter
classes from working.
<CSSTransition in={this.state.showList} timeout={400} classNames="list-transition" unmountOnExit appear exit = {false} >
More lifecycle props in CSSTransition groups
We can use lifecycle props to target specific times in transition phases. These lifecycles do exactly what their names imply:
onEnter
: fires when the button is clicked and the operation is engagedonEntering
: fires when the information is entering the DOMonEntered
: shows that the information has entered the DOMonExit
: essentially fires when the operation for the element exit is initiatedonExiting
: fires when the information is exiting the DOMonExited
: shows that the information has left the DOM
Letās say we need to highlight the most important activity I like to do. We can highlight the color once the list is rendered and add a delay transition before the highlight. Then, our CSSTransition component becomes:
<CSSTransition in={this.state.showList} timeout={400} classNames="list-transition" unmountOnExit appear onEntered= {this.listSwitch} onExit={this.listSwitch} >
The this.listSwitch
state is used to set a conditional class to the hobby we want to highlight. So when highlightedHobby
is true
, weāre going to get this active variant of list item:
<li className={cx('list-item', { 'list-item--active': this.state .highlightedHobby, })} >Writing JavaScript</li>
The conditional className
looks like so:
.list-item--active{ color: blue; transition: 500ms; }
When it opens, we see Writing JavaScript
turn blue after a delay of 500ms
, which is 100ms
later than the transition of the list-item
, and it goes back onExit
. Since this occurs so fast, we canāt see it leave; but if you inspect the element using developer tools, youāll notice it.
Applying Transitions to elements in a list using TransitionGroup and CSSTransition
Using this code example, I will explain its use in creating interesting transitions.
List-Example
List-Example by Ekwuno using classnames, react, react-dom, react-jss, react-router-dom, react-scripts, react-transition-group, styled-components, styled-transition-group
From the code example, we can see that TransitionGroup
maps over the favorite music array and returns each one with a CSSTransition
component.
<TransitionGroup component="ul"> {this.state.favorites.map( ({ id, name }) => ( <CSSTransition timeout={500} classNames="fade" key={id} > <li className="favorite">{name}</li> </CSSTransition> ) )} </TransitionGroup>
From the above code example, we can see that the TransitionGroup
component renders a component, and we can set this to render anything. It could be UL
, div
, p
, option
, etc. But when we do not want to render any component, we can set this to {null}
:
<TransitionGroup component={null}>
Transitions usingĀ JSS
We have been using Vanilla CSS to implement our CSS transitions; now, we will refactor our CSS to become JavaScript objects. We will start by creating a styles.js
file and turning our styles in objects, like so:
// Here we will turn all our Css into JavaScript Objects const startTransitionStyles = { top: 0, width: '120px', maxHeight: '40px', color: 'transparent', backgroundColor: '#5a564c', }; const finishTransitionStyles = { top: '45px', width: '200px', maxHeight: '200px', backgroundColor: '#9e8949', }; const styles = { container: { position: 'relative', }, display: { position: 'relative', zindex: '1', width: '120px', height: '40px', backgroundColor: '#5a564c', border: 'none', borderRadius: '5px', outline: 'none', cursor: 'pointer', transition: 'backgroundColor 350ms', }, displayActive: { backgroundColor: '#000000', }, listBody: { position: 'absolute', top: '45px', Zindex: '1', boxSizing: 'border-box', width: '200px', padding: '0 20px', overflow: 'hidden', backgroundColor: '#9e8949', borderRadius: '5px', }, list: { padding: '0', listStyleType: 'none', }, listItem: { padding: '5px 0', }, listItemActive: { color: 'blue', transition: 'color 500ms', }, listTransitionEnter: { ...startTransitionStyles, }, listTransitionEnterActive: { ...finishTransitionStyles, transition: 'all 400ms', }, listTransitionExit: { ...finishTransitionStyles, }, listTransitionExitActive: { ...startTransitionStyles, transition: 'all 400ms', }, }; export default styles;
The above code snippet is then imported into our refactor.js
as:
import styles from './styles';
The implementation of the styles are done using InjectSheet
imported from react-jss
:
import injectSheet from 'react-jss';
This gives us the classes
props, which we can use to access the styling in style.js
, like so:
render() { const { classes } = this.props; return ( <div className={classes.container}> <button className={classes.display} onClick={this.switch} > Obinna </button> <CSSTransition in={this.state.showList} timeout={400} classNames="list-transition" unmountOnExit classNames={{ enter: classes.listTransitionEnter, enterActive: classes.listTransitionEnterActive, exit: classes.listTransitionExit, exitActive: classes.listTransitionExitActive, }} > <div className={classes.listBody}> <ul className={classes.list}> <li className={classes.listItem}> Writing JavaScript </li> <li className={classes.listItem}> Running </li> <li className={classes.listItem}> Technical Writing </li> <li className={classes.listItem}> Writing Clean code </li> </ul> </div> </CSSTransition> </div> );
Note that we use classNames
here as opposed to className
so we can supply multiple classNames
.
The transitions are added by passing an object with enter
, enter-active
, exit
, and enter-active
keys, and theyāll refer to JSS class names.
<CSSTransition in={this.state.showList} timeout={400} classNames="list-transition" unmountOnExit classNames={{ enter: classes.listTransitionEnter, enterActive:classes.listTransitionEnterActive, exit: classes.listTransitionExit, exitActive:classes.listTransitionExitActive, }} >
CSSTransition component (using JSS)
CSSTransition component (using JSS) by Ekwuno using classnames, react, react-dom, react-jss, react-router-dom, react-scripts, react-transition-group, styled-components, styled-transition-group
Ensure your production React app renders properly
Debugging React applications can be difficult, especially when there is complex state. If youāre interested in monitoring and tracking Redux state for all of your users in production, try LogRocket. https://logrocket.com/signup/
LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.
The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.
Modernize how you debug your React apps – Start monitoring for free.
Conclusion
Smooth transitions make your applicationās user experience more dynamic and welcoming to the end user. React Transition Group helps us achieve this with fewer lines of code that are easier to understand. Happy coding!
Does anyone have any working examples of nested animated routes using RR5.1+? I cant find any example online.