Skip to content

React: How to Design Smooth Page Transitions and Animations

By Sebastian Günther

Posted in React, Css, Tutorial, Bg-app-series

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.

search_button

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.

search_button_highlighted

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:

search_button_highlighted_animation

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 DOM
  • enter-active: The element is active in the DOM
  • exit-active: The element is being removed from the DOM
  • exit: 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 API
    • timeout: 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 all
    • classNames: The prefix of CSS properties that are applied when this component enters or exits the DOM. In this case, I apply a fade effect
    • unmountOnExit: 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 state searched becomes true and triggers this transition
    • timeout: The transition begins 1000 milliseconds after the search results are completed
    • classNames: Again I apply a fade effect
    • mountOnEnter: When the in 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:

search_boardgame_animation

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 prop component defines the default HTML element that will be included in the DOM, setting it to null 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 the key prop. Here, I pass the value of location.key which is defined in the react router history.
  • Line 4-5: The different Route definitions.

Lets see this in action:

page_transition_animation

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.