Lottie vs. Rive: Optimizing Mobile App Animation

When we talk about interactive mobile apps, we often think there'll be using certain user driven animations. For example, we can think of getting a nice order placed animation when we complete a checkout. A more complex example would be onboarding steps where the user has to tap on various buttons to move forward and, in most cases, each step shows a nice animation. So how can developers implement such behavior?

Issue: Real-time animations suffering from low FPS, file size, and not being robust

One approach is using GIFs. If we have 3 onboarding steps, then we will want to have 3 GIFs. And often this solution is good enough performance – and UX-wise. But what if we need more fidelity in our animations? Or when it needs to be high quality, maybe on a full screen? GIFs can quickly add a few megabytes to our app's bundle.

So what other options do we have? Let's talk about Lottie.

Lottie as a viable solution for high-fidelity animations

A mobile client for Android and iOS, which was created by the developers at Airbnb to leverage LottieFiles, which is JSON-based animation exported using the plugin BodyMoving from Adobe AfterEffects. They have a pretty good community with lots of free-to-use animations.

If we look at the React Native Lottie library, it is pretty popular and well-received by the community. We can control the animation using the progress prop or use imperative methods such as play. There are other useful props that we can use to suit our needs. Let's compare the size of a Lottie JSON animation and a corresponding GIF.

screenshot comparing the size of a Lottie JSON animation and a corresponding GIF
Source: https://lottiefiles.com/128635-letter-d

Comparison of Lottie JSON and GIF sizes

If we compare, JSON is 46.2 KB and the GIF is 164.5 KB, which is almost 4 times as much as JSON. We can further reduce the size of JSON using the Optimized Lottie JSON but that's a paid solution. Now if we recall the above approach of having separate GIFs for each onboarding step, we can now use Lottie instead of GIFs since it's smaller. We can also use the remote resources of Lottie JSON instead of having it in a bundle but an extra effort will be required to keep it available for offline purposes.

We can still do better, but how good will that be if we use a single animation and control it using triggers? For example, if we tap on a button, the state of the animation changes for the next step.

Let's say we want to change the size or do other customizations, we can't do it in the editor provided by LottieFiles. Instead, we will have to import that in Adobe AE and then do the adjustments and re-export it as JSON. We can, however, customize the color for layers in the web editor. Not everyone has expertise in using Adobe AE and has an animator to consult, e.g. if we're working on our pet project, we will want to adjust any animation in the web editor.

There are other factors around performance associated with Lottie which we will discuss in the next section.

Is Rive a game-changer in mobile animation?

There's a new tool in town called Rive. It aims to be an alternative to Lottie by providing everything Lottie does and then some. It also ships a web editor to customize real-time animations on the go. The editor allows the user to build interactive animations which have the capability to interact based on the inputs provided by the user. Having trigger-based animations is a great win, especially for mobile platforms.

Solution: Leverage developer-friendly tools which offer better FPS with less file size

Remember the approach we took for the onboarding steps animations? Now if we use Rive animations, we can leverage its state machine, triggers, and user inputs to use a single animation file for our onboarding steps. It really helps in improving the developer experience and the cherry on top: the size of the animation file is very small compared to Lottie JSON. We also don't have to keep different files for each onboarding step; hence saving some KBs for the bundle as well.

import React, { useRef } from 'react';
import { Button, SafeAreaView, StyleSheet } from 'reactnative';
import Rive, { RiveRef } from 'rive-react-native';
const stateMachineOne = 'State Machine 1';
const App = () => {
const riveRef = useRef<RiveRef>(null);
const isRunning = useRef(false);
const onIsRunning = () => {
isRunning.current = !isRunning.current;
riveRef.current?.setInputState(
stateMachineOne,
'isRunning',
isRunning.current,
);
};
view raw rive.javascript hosted with ❤ by GitHub
const onSideKick = () => {
riveRef.current?.fireState(stateMachineOne, 'sideKick');
};
return (
<SafeAreaView>
<Rive
ref={riveRef}
resourceName=''character''
style={styles.character}
stateMachineName={stateMachineOne}
autoplay
/>
<Button title=''Is Running'' onPress={onIsRunning} />
<Button title=''SideKick'' onPress={onSideKick}
color=''#3e3e3e'' />
</SafeAreaView>
);
};
export default App;
const styles = StyleSheet.create({
character: {
width: 400,
height: 400,
},
});

As we see, adding complex animation is developer-friendly. We can also add triggers or input-based animations quickly. All we need is the information regarding the state machine, inputs, and triggers. If we have an animator who built a beautiful animation for our project on Rive Editor, we can ask them to pass along the info. Otherwise, we can always look up this info in the web editor ourselves.

Performance analysis

Now let's talk about performance in terms of FPS, CPU, and memory consumption. We will do a comparison of an animation that is built for Lottie with the same animation built for Rive. This tweet by Rive's CEO shows benchmarks for Lottie and Rive files done for the web platform. We'll extend this by using the same animations for Rive and Lottie on our React Native app.

Lottie

screenshot showing Lottie playing the animation at roughly 17 FPS both on JS and UI threads
Lottie playing our animation at roughly 17 FPS both on JS and UI threads

Rive

screenshot showing Rive playing the animation at roughly 60 FPS both on JS and UI threads
Rive playing our animation at roughly 60 FPS both on JS and UI threads

We benchmarked this on a Sony Xperia Z3 model using an Android profiler and Perf monitor that is shipped with the React Native app. We disabled the DEV mode so that our JS thread doesn't throttle because of it.

Both images show a tiny window right above the rocket with details of FPS on both the UI and JS thread. The results show that the Rive animation runs almost at 60FPS whereas Lottie runs at 17FPS.

Memory consumption

Now let's focus on the right part of both images, which is the Memory consumption detailed view. If we look closely, there are mainly three portions: Java, Native, and Graphics.

Java represents the memory allocated for Java or Kotlin code. The Native represents the memory allocated from C or C++ code. The graphics represent the memory used for the graphics buffer queues to display pixels on the screen. In Java, Lottie uses almost 23 MB and Rive uses almost 12 MB of RAM. In Native, Lottie uses 49 MB and Rive uses 25 MB of RAM. In Graphics, Lottie consumes 123 MB, whereas Rive uses 184 MB of RAM. The total memory consumption of Lottie is 246 MB and Rive is 276 MB.

table with memory consumption in Lottie and Rive

The results show that Rive outperforms Lottie in all departments except Graphics. The end user expects the app to run at 60FPS to enjoy a smooth user experience. If one has to do a trade-off between memory consumption and FPS, they might go with FPS as most of the devices have enough memory to exercise the app's needs.

Benefits of Rive over Lottie

If we opt-in to a world without state machines, the developers will be implementing the logic in their code. And each time there is a change in the interactivity of the animation, devs will be required to re-work their code. This is not a good developer experience.

Rive's state machines give designers the power to think as if they were coding and structure the state machine for an animation that will interact after being given a certain input. Now the developer can use that animation and implement the interactivity firing the inputs on the animation and be done with it. If this animation needs to be changed with the same inputs, the dev only needs to replace the animation source and that's it.

Benefits: A reduced regression cycle while developing a feature and a happy user base

Almost 18.7% of people uninstall the app due to storage issues. This hurts the company's ROI. Developers should always pay attention to reducing the bundle size and the storage utilized by their app.

A tweet by Rive's CEO shows the recreation of Lottie animation in Rive, where the Lottie file is around 24.37 KB and the same Rive file is around 2 KB. At the end of the day, each KB saved adds up to a reduced app size. We always want to choose a library that best fulfills our needs by providing a better developer experience, ease of API, and a smooth experience for the end user.

Insights

Learn more about Performance

cover

Jul 3Podcast

Why Nitro Is More Than a Faster Bridge

Marc Rousavy (Margelo) talks Nitro modules, native state, zero-copy data, Vision Camera, and why speed is only part of the story.

cover

May 25Article

Profile React Components in React Native Release Builds

Learn how to profile React components in React Native release builds with @callstack/inspector, using an Expo app setup that requires zero native code changes.

cover

Nov 21Article

Memory Ownership Models: When JavaScript Meets Native Code

A deep dive into how React Native handles memory when JavaScript and native code share ArrayBuffer data. Learn why async processing can break, how GC affects raw pointers, and the safe patterns for building high-performance native integrations.

cover

Oct 14Article

From Files to Buffers: Building Real-Time Audio Pipelines in React Native

Learn how to move beyond file-based audio APIs and work directly with in-memory PCM data in React Native. This guide shows how to construct WAV buffers, use JSI for zero-copy transfers, and enable real-time audio processing.

cover

Sep 8Article

How to Add Custom JSI Bindings in Your TurboModule (and Why You Might Need Them)

TurboModules provide a type-safe way to write native code in React Native. JSI sits underneath as a lower-level API, enabling direct interaction with the JavaScript runtime. Beyond what TurboModules expose, JSI gives you access to powerful features, such as working directly with ArrayBuffers. This article shows how to inject a custom binding step by step.

cover

Jul 24Article

Exploring Fibers: Cooperative Multitasking and Lock-Free Task Execution

Explore how fibers enable lightweight, cooperative multitasking. This guide walks through building a simple scheduler, managing task dependencies without locks, and using Tracy to visualize performance.

cover

May 21Article

Multithreading Isn’t Free: Performance Pitfalls Visualized

Learn how to profile and optimize multithreaded apps. Discover why more threads don’t always mean faster results and how bad thread contention really is.

Event Cover

Apr 28Webinar

Master JavaScript Performance in React Native (Part 2)

Learn to fix JS performance issues in React Native-from debounce bugs to Redux selectors, in this hands-on webinar with real code examples.

Events Cover

Apr 24Talk

Tips & Tricks From The Ultimate Guide to React Native Optimization 2025

View flattening, React 19 hooks, real DevTools tips-learn what actually helps your React Native app run faster.

Events Cover

Apr 24Talk

User-Focused Performance Tracking

Discover how Expensify defines and tracks performance metrics that actually impact user experience.

cover

Dec 18Podcast

React Native FastIO: Nitro-Powered Performance Boost

Listen to Mike Grabowski talk about React Native FastIO, a collection of primitives for working with data in React Native and its performance benefits.

cover

Oct 15Podcast

Superfast Nitro Modules With Marc Rousavy

Even faster React Native apps? That's possible with Nitro Modules! Tune in to the conversation with their creator, Marc Rousavy.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.