React: How to Design Smooth Page Transitions and Animations
React apps are fast. Changes can be instantaneous. You click on a link, and the next page is there. You execute a search, and the search result table just plops in. It appeared, well, too fast! I was missing smooth transitions: Blend in effects, elements that move into the screen. How to achieve this?
CSS Transitions
Imagine that you have a button. The button is rendered with square borders, and an orange background.
When you hover this button, it changes to rounded corners, and the color becomes lighter. This state change is instantaneous: The new CSS properties are applied immediately.
Instead of this, we can gradually apply the new CSS styles with the CSS transition-property: You define for which properties the transition effect takes place, and you define the delay and the duration of this effect. Let’s see the button changing its style with the enabled transition:
This looks very nice! And it can be realized with only one line of CSS - see the following snipped, last declaration.
.button {
background-color: #db8b49;
color: #000;
border-radius: 0px;
}
.button:hover {
border-radius: 10px;
background-color: #e9b991;
color: #333;
}
.button {
transition: all 2s;
}
Now, let’s see how to apply transitions in a React app.
CSS Transitions in React
In a React app, all changes to the DOM are first realized in the virtual DOM, and then synced with the real DOM through the process called reconciliation.
React keeps track of elements there are entering or leaving the real DOM. Based on this, the library CSSTransition helps to apply styling during this transition. Explained in a nutshell: When an element is added to the DOM, and when it is removed from the DOM, CSS classes are applied to these elements. These classes are:
enter
: The element is entering the DOMenter-active
: The element is active in the DOMexit-active
: The element is being removed from the DOMexit
: The element is removed from the DOM
Let’s apply this library!
Styling board game search
When searching for a board game, my app executes these steps:
- Submit a search to an external API
- Collect API response in a list
- For each item in this list, make another API call to load the board games details
- Render a search result table where each row represents one item
Depending on the search term, the list of items can be anything between 0 and up to 100 games or more. As I said before, the results are rendered instantaneous: The table with all rows just plops in. Sometimes this happens within a second, and sometimes it takes several seconds.
How could we improve this styling? First, if loading takes longer, I want to show a loading spinner. The spinner should also slowly appear, and when the search is completed, it should also slowly disappear and basically make room for the results table. Second, the table should slowly blend into the page, taking about 2 seconds before it’s fully visible. During this transition, I just want to change the opacity of this element.
Let's implement the transition of the loading spinner first.
1 import { CSSTransition } from ‘react-transition-group’;
2
3 <CSSTransition in={loading} timeout={400} classNames=“fade” unmountOnExit>
4 <Loader />
5 <CSSTransition>
- Line 1: Import the
CSSTransition
Component - Line 2: The
CSSTransition
receives four props:in
: A boolean value which, when it evaluates to true, triggers the transitions. In the board game search,loading
is the state from when the user executes the search until the search results are fetched from the APItimeout
: The milliseconds to wait before the transition is applied. In this case, if the search is completed before 400ms, the loading spinner will not be rendered at allclassNames
: The prefix of CSS properties that are applied when this component enters or exits the DOM. In this case, I apply a fade effectunmountOnExit
: This prop defines that when the element is removed from the DOM, it will also be unmounted. This is important! If you do not set this prop, the element will not be rendered again until the user switches the page. In my app, the user might search again, and then the loading spinner needs to be shown again too.
Ok, the loading spinner is covered, Now let’s define a transition for the search result table as well.
1 <CSSTransition in={searched} timeout={1000} classNames=“fade” mountOnEnter unmountOnExit>
2 <>{renderResults()}<>
3 </CSSTransition>
- Line 1: This
CSSTransition
receives five props:in
: When the calls to the external API are finished, the statesearched
becomestrue
and triggers this transitiontimeout
: The transition begins 1000 milliseconds after the search results are completedclassNames
: Again I apply a fade effectmountOnEnter
: When thein
condition becomes true, this component will be mounted or remounted. This is important to apply the fade-in animation again when a new search is done.
- Line 2: The function
renderResults()
returns the search result table
Here is the search board game transition in action:
Styling page transitions
The final piece to make the app smoother is to add CSS transitions when loading a new page. The basic idea is the same: You use <CSSTransition>
to wrap elements. For page transitions, the element that are wrapped are the react router route definitions.
Here is the implementation:
1 <TransitionGroup component={null}>
2 <CSSTransition key={location.key} in={true} classNames="slide-in">
3 <Switch location={location}>
4 <Route path="/bgsearch/" exact render={() => <BgSearch />} />
5 <Route path="/usersearch/" exact render={() => <UserSearch />} />
6 </Switch>
7 </CSSTransition>
8 </TransitionGroup>
- Line 1: The wrapper
<TransitionsGroup>
needs to be applied around all components that will trigger the transition. The propcomponent
defines the default HTML element that will be included in the DOM, setting it tonull
results in not showing an additional element - Line 2: In order to execute the
<CSSTransition>
each time when a new route is rendered, you need to pass an unique value to thekey
prop. Here, I pass the value oflocation.key
which is defined in the react router history. - Line 4-5: The different Route definitions.
Lets see this in action:
Conclusion
CSS transition are an easy to apply methods of adding animations to your page, and with the React library CSSTransition you can add them to your React app. I showed a simple fade-in effect for a loading spinner and a search results table. I also showed how to apply transitions when switching pages in react router. Adding these transitions make your app appear smoother.
Previous: Introduction to React Router