Stunning React Native SVG Animations with Lottie

Nowadays, modern user interface designs involve more animations than ever before. Applying animations to actionable elements like buttons brings greater attention to them. For example, being the first to like a tweet on Twitter fills the heart icon with "+1" and causes plus signs to fly out. When done properly, these animations add an extra dimension of personality to the user interface and effectively communicate complex ideas to users.

React Native lets developers implement animations in their mobile applications via its Animated API. Although an animation can be expressed as a declarative relationship between an input value and output value, writing even the simplest animation requires several steps to be followed:

  1. Declare an initial animated value ("input").

  2. Bind the animated value to the property to be animated (i.e. opacity for fade-in/out animations).

  3. Call a method that corresponds to the type of animation to perform. Will the animation be based on an analytical spring model (Animated.spring)? A timed easing curve (Animated.timing)? A decay coefficient (Animated.decay)? The method takes the initial animated value and changes it over a period of time until the final animated value is reached ("output").

Here's a fade-in animation that illustrates this process:

Try it out for yourself within this Expo Snack.

Often, designers are responsible for creating animations. After all, designers have more experience with tools like Adobe After Effects and decide the creative direction of the user interface. Given the number of lines needed to write a single fade-in animation, imagine how many more lines of code (and developer hours) will be needed to perfectly reproduce animations with lots of elements simultaneously moving around, shrinking, growing, etc. Observing each element frame-by-frame to figure out its unique timing function takes too much time and effort. The end result is unmaintainable code. Why repeat work that's already been done by the designer?

Lottie, Airbnb's library for animations, bridges the designer-developer gap by natively rendering animations created in Adobe After Effects. Animations made by designers can be added to a user interface like static images without any loss in quality. Lottie parses a JSON representation of the animation and automatically renders it. Designers can immediately ship beautiful animations without waiting on a developer to recreate it from scratch.

Below, I'm going to...

  • Show you how to integrate Lottie into a React Native project.

  • Demonstrate how easy it is to render animations with Lottie.

Installation and Setup#

To get started, initialize a new React Native project using the TypeScript template:

At the root of the project directory, create two new directories:

  • assets - Contains static assets like Lottie JSON files.

  • src - Contains React components, providers, utilities, etc.

  • types - Contains global type definitions.

Install the libraries lottie-react-native and lottie-ios (version 3.2.3). These libraries provide support for Lottie in React Native.

Inside of the ios directory, run the following command to install and auto-link the modules lottie-ios and lottie-react-native:

Note: Lottie is compatible with multiple platforms: Android, iOS/macOS, React Native, Web and Windows. Please consult the documentation for platform-specific installation instructions.

The application will display a list of Hacker News stories fetched from the official Hacker News API.

If the user plans on saving a story to read at a later time, then they must press an empty bookmark icon. This will fill the icon (with a single color) and cause small circles to pop out. For now, we'll stub it out with a static bookmark emoji (🔖). Later on, we'll replace it with the proper Lottie animation.

Begin by creating a <HnStoryListItem /> component, which represents a Hacker News story. The component displays the story's title, source URL, score and author. Each story features a bookmark icon that allows the user to bookmark the story.

(src/components/HnStoryListItem.tsx)

Refactor the <App /> component to render a list of Hacker News stories with the <FlatList /> React Native component. Only the ten most recent stories are rendered to the screen.

(App.tsx)

Create a definition file to globally expose several types and interfaces.

(types/index.d.ts)

Note: Inside of the tsconfig.json file, set the typeRoots compiler option to ["./types"].

Verify that everything works by running the application inside of a mobile device simulator (in this case, an iPhone):

Note: If you encounter any linking-related errors, such as the one below, when building the application, then visit this StackOverflow post to resolve the issue.

Adding a Lottie Animation#

To use Lottie, you must export the animation's data from Adobe After Effects as a JSON file via the open-source extension Bodymovin. This JSON file gets passed to Lottie, which then renders the animation.

For developers who don't have any experience with Adobe After Effects, you can download free, open-source Lottie animations from LottieFiles and customize them within LottieFiles' drag-and-drop online editor.

Let's search for a bookmark animation on LottieFiles and download it (as a Lottie JSON file) to the project's assets directory. Pick this bookmark animation by Bryan Trang:

To add a Lottie animation to the React Native project, we must first import the <LottieView /> component from the lottie-react-native library:

(src/components/HnStoryListItem.tsx)

The <LottieView /> component serves as a container for our vector-based Lottie animation.

Now, replace the bookmark emoji with the <LottieView /> component and set the source of the animation to the bookmark animation we just downloaded:

(src/components/HnStoryListItem.tsx)

The autoPlay flag indicates whether or not to play the animation automatically upon being mounted. The loop flag indicates whether or not to continuously run the animation in a loop.

Save the changes. Notice how the animation does not appear. This is because we haven't yet specified the animation's height and width:

(src/components/HnStoryListItem.tsx)

Reload the application. Now the animation should be running! Let's remove some bottom margining from the urlContainer styles to account for the extra space introduced by the animation's larger size.

(src/components/HnStoryListItem.tsx)

Here's what the application should look like:

If a story is not yet bookmarked, then the bookmark icon should be empty, and vice-versa.

Ideally, when the user presses the bookmark icon to bookmark a story, the animation should begin playing. The animation should stop as soon as the circles finish dispersing and the bookmark icon is completely filled.

Lottie's imperative API allows us to play the animation starting at a specific frame and ending at a specific frame:

animation references the <LottieView /> component.

This is important for a few reasons:

  • If we want to present the bookmark icon as being empty, then we just need to find the frame at which it's empty and specify this frame as both the start and end frame of the animation.

  • If we want to animate the bookmark icon and strip out any unnecessary frames, then we just need to include those frames and leave what's not needed out.

Fortunately, we can quickly identify these frames in LottieFiles' online editor:

To implement bookmarking, let's disable auto-playing and looping:

(src/components/HnStoryListItem.tsx)

Then, get a reference to the <LottieView /> component via the useRef hook.

(src/components/HnStoryListItem.tsx)

Nest the <LottieView /> component within a <TouchableOpacity /> component. Set onPress to a function that calls the onBookmarkChange function, which will be passed as a prop to the <HnStoryListItem /> component. onBookmarkChange will either bookmark or unbookmark the story.

(src/components/HnStoryListItem.tsx)

Using the useEffect hook, we can play the animation (bookmarking) or reverse the animation (unbookmarking) whenever the isBookmarked flag has changed. isBookmarked will be passed as a prop to the <HnStoryListItem /> component, and it indicates whether the story has been bookmarked or not.

(src/components/HnStoryListItem.tsx)

When we start up the application, we shouldn't run any bookmarking/unbookmarking animations. All we want to do is immediately show the user either a filled bookmark or empty bookmark per story. Then, whenever the user presses the bookmark icon, an animation will run and the icon will either become filled (if bookmarking the story) or empty (if unbookmarking the story). Let's add a flag to check for the initial mount. Only show either an empty icon or a filled icon (play one frame only) during the initial mount.

(src/components/HnStoryListItem.tsx)

Finally, inside of the <App /> component, add a simple map to track bookmarked stories (in-memory) and set the onBookmarkChange and isBookmarked props on each <HnStoryListItem /> component.

(App.tsx)

Altogether...

(src/components/HnStoryListItem.tsx)

(App.tsx)

(types/index.d.ts)

For a final version of this demo application, check out the source code at this GitHub repository.

Reload the application and try out the animations by pressing on the bookmark icon!

Next Steps#

Add Lottie animations to your React Native projects!

Sources#