lottie-react and react-lottie are both libraries used to render Lottie animations (JSON-based vector animations) within React applications. react-lottie was one of the first ports of the lottie-web player to React, offering a straightforward component-based API. However, it has seen little maintenance in recent years. lottie-react is a modern, actively maintained alternative built specifically for current React versions. It leverages React hooks for better performance, provides a more declarative API, and includes built-in optimizations like automatic cleanup and intersection observer support to pause animations when they are off-screen.
When adding high-quality vector animations to a React app, developers often face a choice between the legacy react-lottie and the modern lottie-react. While both libraries wrap the same underlying lottie-web engine, their approaches to integrating with React differ significantly. One follows modern React patterns with hooks and automatic optimization, while the other relies on older class-based logic that can cause issues in today's ecosystem.
lottie-react is built from the ground up using React hooks. This means it aligns perfectly with functional components and the current React mental model. It handles the mounting and unmounting of the animation player internally, reducing the chance of memory leaks.
// lottie-react: Uses a simple functional component API
import Lottie from "lottie-react";
import animationData from "./animation.json";
const App = () => {
return (
<Lottie
animationData={animationData}
loop={true}
style={{ width: "200px" }}
/>
);
};
react-lottie was designed during the era of class components. While it works in functional components, its internal implementation relies on older lifecycle methods (componentDidMount, componentWillUnmount). This can trigger warnings in React Strict Mode and may not handle rapid re-renders as efficiently.
// react-lottie: Requires importing the player and options object
import Lottie from "react-lottie";
import animationData from "./animation.json";
const App = () => {
const defaultOptions = {
loop: true,
autoplay: true,
animationData: animationData,
rendererSettings: {
preserveAspectRatio: "xMidYMid slice"
}
};
return <Lottie options={defaultOptions} height={200} width={200} />;
};
lottie-react includes built-in performance features that are critical for modern web apps. It supports the useIntersectionObserver logic out of the box. If an animation scrolls out of view, you can easily configure it to pause, saving CPU and battery life on mobile devices without writing extra code.
// lottie-react: Built-in intersection observer support
import Lottie from "lottie-react";
import animationData from "./animation.json";
const App = () => {
return (
<Lottie
animationData={animationData}
play={false} // Starts paused
loop={true}
style={{ width: "200px" }}
// Automatically plays when visible, pauses when hidden
intersectionOptions={{ threshold: 0.5 }}
/>
);
};
react-lottie does not have built-in visibility detection. To achieve the same result, you must manually implement an IntersectionObserver, manage state for the isPlaying flag, and pass it down to the component. This adds significant boilerplate to every animated element.
// react-lottie: Manual implementation required for visibility
import { useEffect, useState, useRef } from "react";
import Lottie from "react-lottie";
import animationData from "./animation.json";
const App = () => {
const [isPlaying, setIsPlaying] = useState(false);
const ref = useRef(null);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
setIsPlaying(true);
} else {
setIsPlaying(false);
}
});
if (ref.current) observer.observe(ref.current);
return () => observer.disconnect();
}, []);
const options = { loop: true, autoplay: isPlaying, animationData };
return <div ref={ref}><Lottie options={options} height={200} width={200} /></div>;
};
lottie-react simplifies event handling by exposing callback props directly on the component. You can listen for when an animation enters a specific frame or completes a loop without needing to access the underlying player instance immediately.
// lottie-react: Direct event props
import Lottie from "lottie-react";
import animationData from "./animation.json";
const App = () => {
const handleComplete = () => {
console.log("Animation finished!");
};
return (
<Lottie
animationData={animationData}
onComplete={handleComplete}
onLoopComplete={handleComplete}
/>
);
};
react-lottie requires you to use a ref to access the player instance if you want fine-grained control (like seeking to a specific frame or changing speed dynamically). The component itself accepts fewer direct event handlers, forcing you to interact with the imperative API of the underlying lottie-web player.
// react-lottie: Using refs for control
import { useRef } from "react";
import Lottie from "react-lottie";
import animationData from "./animation.json";
const App = () => {
const lottieRef = useRef(null);
const stopAnimation = () => {
if (lottieRef.current) {
lottieRef.current.pause();
}
};
const options = { loop: true, autoplay: true, animationData };
return (
<div>
<Lottie options={options} ref={lottieRef} height={200} width={200} />
<button onClick={stopAnimation}>Stop</button>
</div>
);
};
Both libraries depend on lottie-web (often aliased as bodymovin), which is the heavy lifting engine. However, the wrapper code itself differs.
lottie-react is lighter in terms of wrapper logic because it offloads state management to React's native hooks. It tree-shakes well in modern bundlers like Vite or Webpack 5.
react-lottie carries the weight of its legacy class structure. While the difference in kilobytes might seem small, the technical debt of maintaining a library that doesn't follow current React standards is a larger cost for engineering teams.
This is the most critical differentiator.
lottie-react is actively maintained. The repository sees regular updates to fix bugs, support new React versions, and improve TypeScript definitions. It is the recommended choice by the Lottie community for React developers.
react-lottie has not seen significant updates in years. It is considered unmaintained. Using it introduces risk: if a future version of React breaks a legacy lifecycle method, your animations will stop working, and there will be no fix coming from the library author.
| Feature | lottie-react | react-lottie |
|---|---|---|
| React Pattern | ✅ Hooks & Functional Components | ⚠️ Class Components (Legacy) |
| Maintenance | 🟢 Active | 🔴 Inactive / Deprecated |
| Visibility API | ✅ Built-in (intersectionOptions) | ❌ Manual Implementation Required |
| Event Handling | ✅ Props (onComplete, onLoop) | ⚠️ Requires Refs / Imperative API |
| Strict Mode | ✅ Fully Compatible | ⚠️ May Trigger Warnings |
| TypeScript | ✅ Excellent Support | ⚠️ Outdated Definitions |
lottie-react is the clear winner for any professional React project. It treats animations as first-class React citizens, using hooks to manage state and side effects cleanly. It saves development time by providing built-in optimizations like pause-on-scroll, which are essential for performance-conscious applications.
react-lottie is a relic of an earlier era of React. While it might still render animations in a basic setup, it lacks the tooling, maintenance, and architectural alignment required for modern web development. Using it today is a technical debt decision that will likely cost more in debugging and refactoring later than the time saved by not switching libraries now.
Final Recommendation: Always choose lottie-react. It ensures your animation layer remains stable, performant, and compatible with future React updates.
Choose lottie-react for all new projects and active maintenance work. It is the industry standard for React today because it is actively maintained, supports React 18+ features like hooks and concurrent rendering, and offers better performance out of the box. Its API is simpler, requiring less boilerplate to handle animation events, and it automatically manages resource cleanup to prevent memory leaks.
Avoid choosing react-lottie for new development. This package is effectively deprecated and no longer receives updates to match modern React patterns. It relies on older lifecycle methods that can lead to performance issues or warnings in strict mode. Only consider this if you are maintaining a legacy codebase where refactoring to a new library is currently impossible due to resource constraints.
Lottie animations in React: one component for the easy path, the whole engine when you need control.
npm i lottie-react
react and react-dom at 18.2 or newer are peer dependencies; lottie-web comes with it.
The package is tree-shakeable: an import pays only for what it reaches.
import { Lottie } from "lottie-react";
export function Hero() {
return <Lottie src="/hero.json" autoplay loop />;
}
src takes a path, a URL, or the parsed animation object.
The animation fills its element, so size the element and you are done.
import { useLottie } from "lottie-react";
export function Hero() {
const lottie = useLottie({ src: "/hero.json", autoplay: true, loop: true });
return <div ref={lottie.setDisplayRef} style={{ height: 300 }} />;
}
The hook returns the instance: values that re-render with the animation, commands like play and seek, and a subscription point.
Guides, live examples and the full API reference: lottiereact.com.
Coming from v2? The migration guide maps every v2 surface onto v3.
Issues and pull requests are welcome. CONTRIBUTING.md carries the setup and the checks.