Loading…

Micro-interactions with react-spring: Part 3

Article hero image

In the first post in this series, we examined the basics of micro-interactions: what are they, why should you use them, how you build them. In the second post, we built two animated menus.

In this tutorial, we’ll build two fun animations: a modal and an image gallery.

Modal:

Image Gallery:

Building an animated modal

What we’ll build

We’ll use react-spring’s useTransition to illustrate how you can mount and unmount a component, in our case a modal, from the DOM and mount photos to and from a photo gallery.

Embed Code Sandbox:

Defining the micro-interaction

In the previous blog, we looked at the pieces of a micro-interaction. Let’s define the interaction trigger, state definition, and animation definition for our modal component.

Interaction trigger

Our modal will be triggered by a button in the main UI and will be dismissed when the Close button inside of the modal is clicked.

State definition

Our modal will be in one of two states:

  • visible
  • hidden

Animation Definition

When the modal trigger button is clicked, the modal will fade in and translate its Y position from the top of the viewport.

When the modal close button is clicked, the modal will fade out and translate its Y position towards the top of the viewport.

Let’s build a modal

Create a new React Code Sandbox and add react-spring as a dependency.

Next, create a new file in the src directory called Modal.js. The modal will fade in and slide in from the top of the viewport on enter, and fade out and slide out towards the top of the viewport on exit.

If you have an App.js file, you can delete it—we’ll be working in index.js. You’ll also have to remove the import to App.js and the instantiation of the component inside index.js. Replace index.js with the following code:

index.js:

Create a new file called modal.css in the src directory and add the following code. We won’t cover styling in this tutorial, so we’ve provided everything you need to focus on the animation.

Modal.css:

Let's also add a style to styles.css:

Styles.css:

Inside Modal.js import React, { animated } from "react-spring" and modal.css.

Modal.js:

For now, let's simply return a <div> with "Modal" written inside to ensure everything is rendering as expected.

Modal.js:

The majority of the animation work will be done in index.js because this is where our state will live.

In index.js import our Modal component, useTransition from react-spring, and add useState to the React import.

Index.js:

If you have them, remove the Code Sandbox h1 and h2 elements and instantiate our Modal component.

Index.js:

You should see "Modal" rendering in the UI.

Let's add some state to show and hide our modal. To show our modal, we'll have a "Show modal" button. Since our modal will not be visible by default, we'll initialize the modalVisible state to false.

Before the return statement inside of App, add the following:

Index.js

Now let's build our animation using react-spring's useTransition which mounts and unmounts a component from the DOM.

Underneath the useState hook, let's declare a fadingAnimation const and set it equal to the result of calling useTransition.

Index.js:

This hook takes three arguments:

  1. The first argument is the state this animation is dependent on, in our case modalVisible.
  2. The second argument is the keys for each item. React requires keys for elements which are being mapped over, and since the result of calling the useTransitionhook must be mapped over, we must pass a value for key, which in our case is null (since we just have one item, the modal).
  3. The third argument is the lifecycle definition. The lifecycle definition object takes several required properties. We have to define these three properties to use useTransition:
  • from
  • enter
  • leave

You can view the full list of optional properties here.

Each of these three values expects an object with the CSS properties that change during the animation.

We want our modal to transition from an opacity of 0 and start outside of the viewport. When the modal enters, we want to fade it in by changing the opacity to 1 and slide in from the top by translating the Y-property. And on leave we want the modal to fade out and slide out towards the top.

Index.js:

The useTransition hook requires its results to be mapped over in the render function. So let's replace the simple <Modal /> with a JSX expression which maps over fadingAnimation.

We can de-structure item, key, and props out of fadingAnimation. props is a bit confusing as it's actually the animation we're going to be passing to our modal component, so let's rename it style to make it more clear.

We will pass style as an argument to our modal component, as well as key.

We also need to pass a function which will close the modal by updating the modalVisible state when the Close modal button is clicked.

Index.js:

Now let's add a button to show the modal. Before the transition mapping, add the following.

Index.js:

Here is our finalized index.js

Index.js:

When the UI is rendered we see a button:

And when we click it, we see the word “Modal”, but there is currently no animation.

Now let's build our modal. This will take two arguments:

  • style: The animation
  • closeModal: The event handler for closing the modal

Modal.js:

The modal will contain a title, some content, and a close button, so let's add those, as well as some class names for styling.

Modal.js:

Let's pass our closeModal function to the close button as an on click handler so the user can dismiss the modal.

Modal.js:

If we head back to the browser, we can see our modal appears and closes, but isn't being animated.

This is because we have to use a special animated HTML element to get our modal to animate. Any element we want to be animated must be prefaced by animated., for example <animated.div>. So let's change our <div> elements into animated divs.

Modal.js:

Lastly, we have to pass style to <animated.div> as a prop to see our animation.

Modal.js:

And that's it! You now have a fully-animated modal which mounts and unmounts when it's shown and hidden.

View the final code here:

Building an image gallery

Making an image gallery with plain JavaScript, and animating it, can be tedious. But not with react-spring! Let's make an image gallery that switches between three photos.

What We’ll Build

The end code:

Defining The micro-interaction

Interaction trigger

The gallery will transition to the next image when the user clicks anywhere in the viewport.

State definition

We will have three images in our gallery so its state will be one of the following states, each number representing the image currently being shown.

  • 0
  • 1
  • 2

Animation definition

The leaving image will fade out and slide to the left while the entering image will fade in and slide in from the right.

Let’s build an image gallery

Let's make an image gallery that switches between three photos.

Create a React Code Sandbox and add react-spring as a dependency.

If you have an App.js file, you can delete it—we’ll be working in index.js. You’ll also have to remove the import to App.js and the instantiation of the component inside index.js. Replace index.js with the following code:

index.js:

Create two new files: Gallery.js and gallery.css and add the following code to gallery.css. We won’t be covering CSS today so we’ve provided the styling for you!

Gallery.css:

Create an images folder in the root and add three photos.

You can find some on Unsplash. I added mountains.jpg, beach.jpg, and desert.jpg.

Inside of Gallery.js, import the following packages, as well as the three images you just uploaded, and the CSS file.

Gallery.js:

Now let's define three images we want to iterate between. Each will take in a style and render an animated.img tag (as react-spring requires any animatable element to be prefaced with animated. ) with its respective image source, alt tag, and style.

Gallery.js:

Next, let's set up our component.

Gallery.js:

First we need to establish our state for which photo we're currently viewing. Above the return statement, we'll call this state index and it's setter function setIndex, and initialize the value to 0 for the first image.

Gallery.js:

When the user clicks anywhere in the viewport, we want to switch to the next image. We can use useCallback to do this.

Since we have three images, we want to ensure the state value remains either 0, 1, or 2, so we can use modulus to grab the remainder of the next state value divided by three.

For example if the state is currently 2 and we want the next photo, we add 1, which totals 3. Since 3 is outside the bounds of the number of photos we have (indexing from 0, we have 0, 1 and 2 for available photo indices), let's mod it by 3 to get the remainder.

(2 + 1) % 3 = 0 because 3 mod 3 yields a remainder of 0.

You can read more about mods here.

Gallery.js:

Let's add our onClick handler as an argument on the wrapping <div> so the user can switch to the next photo by clicking in the viewport.

Gallery.js:

Next, let's set up our animation. We'll use useTransition to mount and unmount the photos from the DOM so we're only rendering one at a time.

useTransition takes in three arguments:

  • item: The index for our photo
  • key: The key for our mapped over item
  • lifecycle: An object describing the animation

We want our images to fade in and out while sliding them into and out of view so we'll define opacity and transform at each of the lifecycle steps.

We will also add a config option to change the speed at which this animation occurs. You can check the full options for config here.

Gallery.js:

Now we're ready to add some JSX. Let's first render a paragraph that tells the user they can click anywhere to change to the next image.

Gallery.js:

Since useTransition requires we map over its return value, let's map over our transitions and render each image. We'll de-structure item, props, and key from transitions.

Then we'll grab the image component from our images array by using the item, or index defined in useTransition.

Finally we'll return the Image component and pass it the key and props (as the style attribute).

Gallery.js:

Here is our completed Gallery.js:

Finally let's head back to index.js to import our Gallery component and render it.

index.js:

And that's it! You now have a fully-functional image gallery.

You can view the final code here:

Conclusion

I hope this series of blog posts on creating micro-interactions with react-spring has empowered you to create fun interactions within your website.

You can create complex animations and interactions with just a few lines of code.

For more articles on micro-animations and micro-interactions check out the resources below.

Login with your stackoverflow.com account to take part in the discussion.