react-lottie
v2.4.2
Published
lottie animation view for React
Readme
Lottie Animation View for React (Angular, Vue)
This project is fully maintained automatically by Relay.
Demo
https://chenqingspring.github.io/react-lottie
Wrapper of lottie-web
lottie-web (previously Bodymovin) is an Adobe After Effects plugin for exporting animations as JSON. It also provides the player that renders them as svg/canvas/html.
Why Lottie?
Flexible After Effects features
We currently support solids, shape layers, masks, alpha mattes, trim paths, and dash patterns. And we’ll be adding new features on a regular basis.
Manipulate your animation any way you like
You can go forward, backward, and most importantly you can program your animation to respond to any interaction.
Small file sizes
Bundle vector animations within your app without having to worry about multiple dimensions or large file sizes. Alternatively, you can decouple animation files from your app’s code entirely by loading them from a JSON API.
Learn more › http://airbnb.design/lottie/
Looking for lottie files › https://www.lottiefiles.com/
Installation
Install through npm:
npm install --save react-lottieNote: Found a bug or have a feature request? Open an issue on GitHub and Relay will review and fix it.
Using from a CDN (no bundler)
The published dist/index.js is a CommonJS build for bundlers and npm
consumers. It uses exports and expects react, prop-types and lottie-web
to be resolvable, so loading that file with a plain <script> tag fails with
Uncaught ReferenceError: exports is not defined. The package does not ship a
UMD/browser global build.
To use react-lottie without a build step, load it as an ES module from a CDN
and map react to the same copy the page uses. esm.sh
supports ?external=react for exactly this:
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18",
"react-dom/client": "https://esm.sh/react-dom@18/client",
"react-lottie": "https://esm.sh/react-lottie@2?external=react"
}
}
</script>
<script type="module">
import React from 'react';
import { createRoot } from 'react-dom/client';
import Lottie from 'react-lottie';
const options = {
path: '/animations/pinjump.json',
loop: true,
autoplay: true,
};
createRoot(document.getElementById('root')).render(
React.createElement(Lottie, { options, height: 400, width: 400 })
);
</script>Without a compiler for JSX, create elements with React.createElement, or add
htm/Babel. Keeping react in the import map gives the page one React
instance; a CDN build that bundles its own React (such as jsDelivr's +esm
output) produces two copies, which breaks context and hooks.
If you only need to play an animation and not the React wrapper, load lottie-web directly and skip React entirely:
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js"></script>
<script>
lottie.loadAnimation({
container: document.getElementById('root'),
path: '/animations/pinjump.json',
renderer: 'svg',
loop: true,
autoplay: true,
});
</script>An ESM CDN build still downloads React, React DOM, lottie-web and react-lottie, so it removes the build step but not the bytes. For production, a bundler that shares one React copy and tree-shakes lottie-web is smaller.
Requirements
React 16.8 or newer, including React 17, React 18 and React 19. The
react: ">=16.8.0" peer dependency resolves without --legacy-peer-deps or
--force. The test suite runs against React 18; the component uses plain class
components and works with React 17 and React 19 as well.
There is no npm version requirement. 1.x declared
"engines": { "npm": "^3.0.0" }, so npm 6 and newer printed an
Unsupported engine warning on install; that field was removed before 2.0.0
(#87). Install with
any npm version that can resolve the React peer dependency.
The component is a class, but it only uses the lifecycles React still supports
(componentDidMount, componentDidUpdate, componentWillUnmount). The
componentWill* methods that React 16.3 deprecated and React 17 stopped
calling were removed in 2.0.0, so React 17 and newer do not warn about legacy
lifecycles or skip the data/path reload (#77,
#119).
Local development
Run the Storybook stories with npm start (port 9010) and the test suite with
npm test.
TypeScript
Type definitions are maintained on DefinitelyTyped and track this package's props. Install them alongside the package:
npm install --save-dev @types/react-lottieTypeScript picks the declarations up automatically, so no import changes are needed.
Testing
lottie-web probes canvas support when it is imported, so a jsdom-based test
runner needs a canvas implementation before the component is loaded. With Jest
(the default for Create React App), add jest-canvas-mock
to your setup file:
import 'jest-canvas-mock';Usage
Import pinjump.json as animation data
import React from 'react'
import Lottie from 'react-lottie';
import animationData from './pinjump.json'
export default class LottieControl extends React.Component {
constructor(props) {
super(props);
this.state = {isStopped: false, isPaused: false};
}
render() {
const buttonStyle = {
display: 'block',
margin: '10px auto'
};
const defaultOptions = {
loop: true,
autoplay: true,
animationData,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
}
};
return <div>
<Lottie options={defaultOptions}
height={400}
width={400}
isStopped={this.state.isStopped}
isPaused={this.state.isPaused}/>
<button style={buttonStyle} onClick={() => this.setState({isStopped: true})}>stop</button>
<button style={buttonStyle} onClick={() => this.setState({isStopped: false})}>play</button>
<button style={buttonStyle} onClick={() => this.setState({isPaused: !this.state.isPaused})}>pause</button>
</div>
}
}
Pass the parsed animation object as options.animationData. A file path or URL
belongs in options.path; a namespace import
(import * as animationData) is unwrapped automatically but the default import
above is preferred.
Loading animation data from a URL
Put the URL in options.path, not options.animationData. animationData is
the parsed JSON object; lottie-web cannot fetch a string, so react-lottie throws
an explanatory error if you pass one (issue #106):
const defaultOptions = {
path: 'https://someurl.com/animationData.json',
loop: true,
autoplay: true,
};
<Lottie options={defaultOptions} width="1293px" height="300px" />path accepts an absolute URL, a site-root path or a relative path. lottie-web
fetches it after mount (nothing is requested during server rendering), and
changing path reloads the animation. Because lottie-web owns the request,
colorFilters is not applied — fetch the JSON yourself and pass the parsed
object as animationData if you need to recolor it or send request headers:
import { useEffect, useState } from 'react';
function RemoteAnimation() {
const [animationData, setAnimationData] = useState(null);
useEffect(() => {
let cancelled = false;
fetch('https://someurl.com/animationData.json')
.then((response) => response.json())
.then((data) => { if (!cancelled) setAnimationData(data); });
return () => { cancelled = true; };
}, []);
if (!animationData) return null;
return (
<Lottie options={{ animationData, loop: true, autoplay: true }} height={300} />
);
}See Loading animation data without bloating the bundle for the bundle-size trade-offs between these approaches.
Loading animation data without bloating the bundle
A static import like import animationData from './pinjump.json' (the example
above) makes the bundler inline the animation into the JavaScript chunk that
imports it. A 500 kB JSON file then becomes 500 kB of JavaScript the browser has
to download and parse before the page can run. The JSON is data, not code, so
keep it out of the main bundle:
Serve it from public/ and use options.path. In Create React App, Vite
and Next.js, files in public/ are copied to the build output as static
assets; they are not bundled into JavaScript. lottie-web fetches the file when
the component mounts, and the browser caches it separately from the app bundle:
const defaultOptions = {
path: '/animations/pinjump.json', // relative to the deployed site root
loop: true,
autoplay: true,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
}
};
<Lottie options={defaultOptions} height={400} width={400} />Because lottie-web owns the fetch, colorFilters is not applied to animations
loaded this way — fetch the JSON yourself if you need to recolor it.
Or load it on demand with a dynamic import(). If you need the parsed data
in your own code, a dynamic import puts the JSON in a separate chunk that is
downloaded only when the component renders:
import { useEffect, useState } from 'react';
import Lottie from 'react-lottie';
export default function Animation() {
const [animationData, setAnimationData] = useState(null);
useEffect(() => {
let cancelled = false;
import('./pinjump.json').then(({ default: data }) => {
if (!cancelled) setAnimationData(data);
});
return () => { cancelled = true; };
}, []);
if (!animationData) return null;
return (
<Lottie
options={{ animationData, loop: true, autoplay: true }}
height={400}
width={400}
/>
);
}Rendering <Animation /> lazily (for example when it scrolls into view) defers
even that chunk until it is needed. The same idea works with React.lazy around
a component that imports the JSON.
Finally, the file itself can usually get smaller: export at a lower precision, remove unused layers and precompositions, avoid embedded raster images, and let gzip/Brotli compress the JSON. LottieFiles' optimizer automates the first steps.
Animations with raster images
An After Effects export that contains bitmap layers references those files from
the animation's assets array. Each entry carries a directory (u), a file name
(p) and an embedded flag (e):
{ "id": "image_0", "w": 720, "h": 720, "u": "images/", "p": "img_0.png", "e": 0 }With "e": 0 lottie-web loads the file separately and resolves u + p relative
to the page, which breaks as soon as the app is served from a sub-path or
navigates client-side. Set options.assetsPath to the directory where the
images are deployed instead; react-lottie forwards unknown options to
lottie-web:
const defaultOptions = {
path: '/animations/cypher.json',
assetsPath: '/animations/images/', // trailing slash required
loop: true,
autoplay: true,
};
<Lottie options={defaultOptions} height={400} width={400} />Copy the images next to the JSON in public/animations/images/ so they are
served as static files instead of being bundled. assetsPath works with both
path and animationData, and it applies to every image in the animation.
If you want the bundler to hash and inline the images, import them yourself and rewrite the asset entries before passing the data. Don't mutate the imported JSON in place when it is a frozen ESM object:
import img0 from './images/img_0.png';
import animationData from './cypher.json';
const data = {
...animationData,
assets: animationData.assets.map((asset) => (
asset.id === 'image_0' ? { ...asset, u: '', p: img0 } : asset
)),
};
<Lottie options={{ animationData: data, loop: true, autoplay: true }} />"e": 1 means the image is embedded in the JSON as a base64 data URI. That
keeps the animation self-contained, but it inflates the file by roughly a third
and prevents the browser from caching the image separately, so prefer external
images for anything large.
Invalid or unexpected token with embedded images
Embedded base64 images do work: an asset with "e": 1 and a p value that
starts with data:image/png;base64, is decoded like any other image, and the
layer only has to reference it through refId.
The Uncaught SyntaxError: Invalid or unexpected token thrown from
eval (eval at initiateExpression ...) is not about the image. It comes from an
expression inside the animation. lottie-web compiles expressions with
eval when the animation loads. Newer After Effects versions can save an
expression in an encoded @JSXBIN@… form, which lottie-web cannot parse, so
the eval throws while the layer's effects are prepared
(IImageElement.prepareProperties). The same error can appear in animations
with no images at all.
Fixes, in order of preference:
- Remove the expressions in After Effects, or bake them to keyframes (Animation → Keyframe Assistant → Convert Expression to Keyframes), then export again. The animation keeps looking the same without runtime evaluation.
- Keep the expression only if it is plain JavaScript, and make sure a Content
Security Policy is not blocking
eval(it needs'unsafe-eval'). Otherwise move that behaviour out of the animation. - If the expression is not needed for playback, alias
lottie-webto one of the light builds: they have no expression engine and skip expressions instead of throwing. They skip the effect's behaviour too, so only do this when the visual result is acceptable.
Shrinking the lottie-web bundle
react-lottie imports the full lottie-web player (299 kB minified, 611 kB
unminified in lottie-web 5.13), which includes the expression engine and every
renderer. If your animations don't use expressions and you only need one
renderer, alias lottie-web to a smaller build; react-lottie keeps working
because those builds expose the same loadAnimation API.
Vite:
// vite.config.js
export default defineConfig({
resolve: {
alias: {
'lottie-web': 'lottie-web/build/player/lottie_light.js',
},
},
});webpack (Create React App, Next.js):
// next.config.js
module.exports = {
webpack(config) {
config.resolve.alias['lottie-web'] = require.resolve(
'lottie-web/build/player/lottie_light.js'
);
return config;
},
};The light builds are about 45% smaller (164 kB vs 299 kB minified):
lottie_light.js— SVG renderer only, no expressions; shapes and raster images still work.lottie_light_canvas.js— canvas renderer only, no expressions.lottie_light_html.js— HTML renderer only, no expressions.
Pick the build that matches options.renderer: lottie_light only registers
the SVG renderer, so pass renderer: 'svg' (the default); use
lottie_light_canvas with renderer: 'canvas'. Don't alias when an animation
uses expressions or effects, because the light builds silently skip them.
Performance on iOS and low-end devices
File size mostly affects how long an animation takes to download and parse, not its frame rate. Once playback starts, the cost of each frame comes from what the renderer has to draw: the number of layers and shapes, masks and mattes, expressions, effects, and the pixel size of the canvas. A multi-megabyte export is a good signal that there is a lot of that (issue #133).
On iOS 14.5, Safari, Chrome and Firefox all run on WebKit, so they render at the same speed; measuring in Safari is representative. Phones are also much slower than a desktop, so an animation that is smooth on a laptop can drop frames on mobile.
In rough order of impact:
- Reduce the animation. Remove hidden and unused layers, flatten
precompositions, simplify masks and mattes, drop expressions and effects, and
avoid embedded raster images. If one scene is still heavy, split it into
smaller files and play only the needed range with
segments. - Draw it at the size it is displayed.
widthandheightset the container size; rendering a 1200 px animation into a 300 px slot does more work than necessary. With the canvas renderer, also cap the backing-store resolution: at a device pixel ratio of 3, a 400 px canvas is rasterized at 1200 px unless you setdpr. - Compare the renderers.
svg(the default) is usually fastest for a few large shapes and stays crisp at any size.canvasavoids updating hundreds of DOM nodes and often wins for animations with many small shapes. Measure both on the target device. - Stop off-screen animations. lottie-web drives every playing animation
from the same
requestAnimationFrameloop, so an animation scrolled out of view still costs a frame. Pause it withisPaused, or withpause()on aref, driven by anIntersectionObserver.
const defaultOptions = {
animationData,
loop: true,
autoplay: true,
renderer: 'canvas',
rendererSettings: {
dpr: 1, // render at CSS resolution; use 2 for a sharper middle ground
},
};
<Lottie options={defaultOptions} height={300} width={300} />For the default svg renderer, rendererSettings.progressiveLoad builds the
layers over several frames instead of blocking on the first one. It does not
wait for the element to scroll into view; pause off-screen animations yourself:
// `visible` is kept up to date by an IntersectionObserver on a wrapper element.
<Lottie
options={{ animationData, loop: true, autoplay: false, rendererSettings: { progressiveLoad: true } }}
isPaused={!visible}
/>If a reduced file still drops frames, profile it: record a timeline in Safari's Web Inspector while the animation plays and check whether the time goes to lottie-web's frame update or to the browser painting and compositing the layers.
Fit or crop an animation (preserveAspectRatio)
The SVG that lottie-web generates has its own viewBox, and the browser scales
it to the container according to preserveAspectRatio. lottie-web's default is
xMidYMid meet, which letterboxes the animation. Use xMidYMid slice to fill
the container and crop the overflow instead, or none to stretch it.
rendererSettings belongs inside options, next to animationData or
path. A rendererSettings prop on <Lottie /> itself is ignored, because the
component only forwards options to lottie.loadAnimation():
const defaultOptions = {
animationData,
loop: true,
autoplay: true,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice',
},
};
// A wide box for a square animation, so `slice` has something to crop.
<Lottie options={defaultOptions} height={200} width={400} />Two things make a working setting look broken:
meetandslicerender identically when the container has the same aspect ratio as the animation. Sizing a square animation withheight={400} width={400}leaves nothing to crop or letterbox.- The value is applied when the animation loads. Changing
options.rendererSettingson a mounted component does not re-apply it; remount the animation (or change itskey) to pick up a new value.
If the <svg> in the inspector still lacks preserveAspectRatio, check what
you pass as options.animationData. An ES module namespace
(import * as animationData) is unwrapped for you, but any payload lottie-web
cannot read leaves it without a viewBox, so the attribute has nothing to
scale. See Usage.
The canvas renderer reads preserveAspectRatio too.
Masks are missing in Safari
Safari resolves the url(#…) references that lottie-web generates for masks,
mattes, gradients and filters against the document's base URL. When the page
has a <base href> tag, or the animation is served from a path the browser
resolves differently, those references point at the wrong document and Safari
renders the animation without its masks. Chrome and Firefox usually still find
them.
lottie-web's documented workaround is to call lottie.setLocationHref() before
the animation is generated. react-lottie exposes it as options.locationHref:
const defaultOptions = {
animationData,
loop: true,
autoplay: true,
locationHref: window.location.href,
};
<Lottie options={defaultOptions} height={400} width={400} />Pass the URL the page is actually served from, without the # fragment.
lottie-web joins the value with #<id> when it builds the references, so a
fragment produces broken URLs. The value is global to lottie-web, so pass the
same locationHref to every <Lottie /> on the page. Changing it regenerates
the animation, because the base URL is baked into the generated markup.
If you already import lottie-web yourself, calling the function directly works too, as long as the bundler resolves it to the same copy react-lottie uses:
import lottie from 'lottie-web';
lottie.setLocationHref(window.location.href);Run that before the first animation mounts, for example at application startup.
If the masks are still missing with the correct href, the artwork is the likely cause: complex masks and alpha mattes are the least reliable part of the SVG renderer. Compare with the canvas renderer, or open the same JSON in lottie-web's own preview to confirm the problem is renderer-level rather than a react-lottie bug.
Play on hover
eventListeners takes lottie-web animation events (complete, loopComplete,
enterFrame, ...), not DOM events. Registering onMouseOver or onMouseLeave
there will never fire. Put the DOM handlers on a wrapper element and drive the
component with props instead.
To play forward on hover and reverse from the current frame on leave, keep the
animation running and flip direction. isStopped resets the animation to its
first frame, so only use it once, to start from a known state.
import React from 'react'
import Lottie from 'react-lottie';
import animationData from './pinjump.json'
export default class HoverLottie extends React.Component {
constructor(props) {
super(props);
this.state = {isStopped: true, direction: 1};
}
render() {
const defaultOptions = {
loop: false,
autoplay: false,
animationData,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
}
};
return <div
onMouseEnter={() => this.setState({isStopped: false, direction: 1})}
onMouseLeave={() => this.setState({direction: -1})}>
<Lottie options={defaultOptions}
height={100}
width={100}
isStopped={this.state.isStopped}
direction={this.state.direction}
isClickToPauseDisabled />
</div>
}
}
Click to pause
Clicking or tapping the animation pauses it, and clicking again resumes it.
This is on by default: the container also has ariaRole="button", so an
animation that looks decorative is still an interactive control (issue
#110).
Disable it when the animation is decorative, or when you want a click to do something else:
<Lottie options={defaultOptions} isClickToPauseDisabled />A decorative animation should also leave the tab order. Pair
isClickToPauseDisabled with tabIndex={-1} so keyboard users do not land on
an animation they cannot interact with (issue
#130):
<Lottie options={defaultOptions} isClickToPauseDisabled tabIndex={-1} />The component has no onClick prop. Put your handler on a wrapper element
instead; the click bubbles from the animation container to the wrapper:
<div onClick={() => openDetails()}>
<Lottie options={defaultOptions} isClickToPauseDisabled />
</div>Without isClickToPauseDisabled both run: playback toggles and the wrapper's
handler still fires after it. Leave the prop out when you want click-to-pause as
well.
Play a segment
Pass segments to play a frame range instead of the whole animation. The
frames are the ones shown in After Effects / the lottie editor.
<Lottie options={defaultOptions}
height={400}
width={400}
segments={[45, 90]} />Changing segments while the animation is running restarts playback from the
new fromFrame, so a single state value is enough to switch ranges:
<Lottie options={defaultOptions} segments={this.state.segment} />A segment also has to be reachable with loop/autoplay as usual: with
loop: false and autoplay: false the range only plays once you flip
isStopped or pass a new segments value.
Recolor an animation
colorFilters replaces the colors of fills, strokes and solid layers named by
a keypath, so one animation file can follow a theme (issue #144):
<Lottie
options={defaultOptions}
colorFilters={[
{ keypath: 'Pin 1.Shape 2.Fill 1', color: '#4f46e5' },
{ keypath: 'Background', color: 'rgb(15, 23, 42)' },
]}
/>A keypath is the dot-separated name chain from the layer down to the fill,
stroke or solid, e.g. layer.group.fill. For a layer inside a precomposition,
the composition name can be included (Precomp.InnerLayer.Shape.Fill 1) or
omitted. * matches one level and ** matches any number of levels; dots in
a name can be escaped with a backslash. Colors accept hex, rgb()/rgba(),
hsl()/hsla() and the basic CSS color names. Invalid filters are ignored.
The filters are applied to options.animationData. Changing them reloads the
animation, because lottie-web cannot recolor a loaded animation; the current
frame and paused/playing state are preserved. Animations loaded from
options.path are not recolored, since the JSON is fetched by lottie-web:
load the JSON yourself and pass it as animationData instead.
Render inline inside a paragraph
The container is a div by default, which is not a valid child of <p>. Pass
as to choose the element (issue #73):
<p>
Text <Lottie as="span" options={defaultOptions} ariaRole="img" tabIndex={-1} /> more text
</p>as accepts a tag name or a component that forwards its ref to the DOM node
lottie-web should render into. Changing as reloads the animation into the new
element and keeps the current frame.
Switch between several animations
Each <Lottie> owns one lottie-web instance, so an app can render two
animations side by side or keep one animation per page. To choose which one is
shown, keep the animation data in a map and pass the entry for the current page
as options.animationData (issue #128):
import homeData from './animations/home.json';
import aboutData from './animations/about.json';
const animations = { home: homeData, about: aboutData };
const PageAnimation = ({ page }) => (
<Lottie
options={{
loop: true,
autoplay: true,
animationData: animations[page],
}}
height={300}
width={300}
/>
);Changing options.animationData reloads the animation into the same container,
so a key is not required. Keep the JSON modules stable: copying the data in
render (animationData: { ...animations[page] }) hands the component a new
object on every render and reloads the animation each time. If you do want to
start over — to replay an intro, for example — give the component a key so
React unmounts and remounts it:
<Lottie key={page} options={{ ...defaultOptions, animationData: animations[page] }} />Unmounting destroys the lottie-web instance, so only the visible page animates.
When the files are large, load them with options.path and fetch the JSON for
the next page ahead of time instead of shipping every animation in one bundle;
see Loading animation data without bloating the bundle.
Server-side rendering
<Lottie /> renders only a sized container element on the server; lottie-web is
imported but instantiated in componentDidMount only, so renderToString (and
Next.js/Gatsby prerendering) works and the animation starts after hydration:
// server output: <div style="width:400px;height:400px;..."></div>
<Lottie options={defaultOptions} height={400} width={400} />Importing the package outside a browser no longer throws
ReferenceError: window is not defined; lottie-web guards its DOM access at
module load. If your bundler still evaluates the player on the server, load the
component without SSR:
// Next.js
import dynamic from 'next/dynamic';
const Lottie = dynamic(() => import('react-lottie'), { ssr: false });Animations loaded from options.path are fetched by lottie-web after mount, so
nothing is requested during the server render. Avoid the hydration trap
described in Animations and page navigation
when the container is nested in a <p>.
Animations and page navigation
react-lottie creates a lottie-web instance when the component mounts and calls
destroy() on unmount, so client-side navigation is safe as long as the page
really unmounts. A layout that renders every page at once (or keeps pages
mounted when switching tabs) keeps every animation running; mount only the
visible one.
One non-obvious trap on SSR frameworks such as Gatsby or Next.js: the wrapper is
a div by default, and a div inside a p is invalid HTML. The browser moves
the div out of the paragraph while parsing the server-rendered page, React
reports a hydration mismatch (validateDOMNesting), and the animation node ends
up outside the page subtree. Navigating back and forth can then leave animation
nodes behind. Render the animation with as="span" (issue #73) or move it
outside the paragraph:
<p>
Text <Lottie as="span" options={defaultOptions} /> more text
</p>If animations still accumulate, confirm that each page unmounts (React DevTools shows the component tree changing), and pause off-screen instances as described in Performance on iOS and low-end devices.
Imperative control
A ref to the component exposes play(), pause(), stop(),
playSegments(), goToAndStop(frame, isFrame) and
goToAndPlay(frame, isFrame), mirroring the lottie-web animation object. Use
isFrame: true for a frame number, or omit it to pass a time in seconds. This
is how you can sync an animation to scroll position (issue #40):
const lottie = useRef(null);
const onScroll = (frame) => lottie.current.goToAndStop(frame, true);
<Lottie ref={lottie} options={defaultOptions} />props
The <Lottie /> Component supports the following props:
options required
the object representing the animation settings that will be instantiated by bodymovin. Currently a subset of the bodymovin options are supported:
loop options [default:
false]autoplay options [default:
false]animationData optional — the exported animation data. Mutually exclusive with
path.path optional — a URL (or path) to a JSON animation file. Mutually exclusive with
animationData.renderer optional [default:
svg] —svg,canvasorhtml. See Performance on iOS and low-end devices.rendererSettings optional — settings forwarded to lottie-web's renderer, such as
preserveAspectRatio,progressiveLoad,classNameandid. Must be insideoptions, not a prop on<Lottie />. Read once when the animation loads. See Fit or crop an animation.locationHref optional — the base URL lottie-web writes into its SVG mask, matte, gradient and filter references, applied with
setLocationHref()before the animation is generated. Use it when Safari drops masks because of a<base>tag. See Masks are missing in Safari.
colorFilters optional [default: []]
Array of { keypath, color } objects that replace the colors of fills,
strokes and solid layers. See Recolor an animation.
width optional [default: 100%]
pixel value for containers width.
height optional [default: 100%]
pixel value for containers height.
margin optional [default: 0 auto]
Margin of the container. Accepts any CSS margin value ('20px',
'10px auto', '1rem 2rem') or a number of pixels. Use it instead of
style.margin when the default 0 auto misbehaves in a browser (issue #107).
style is merged after this prop, so an explicit style.margin still wins.
<Lottie options={defaultOptions} margin="10px auto" />isStopped optional [default: false]
Stops the animation and resets it to the first frame. Setting it back to false
resumes playback. Unlike isPaused, this discards the current position.
isPaused optional [default: false]
Pauses the animation without losing its current frame. Setting it back to
false resumes from where it stopped.
speed optional [default: 1]
Playback rate. 2 plays twice as fast, 0.5 at half speed, and negative
values play in reverse at that rate.
direction optional [default: 1]
1 plays forward, -1 plays backward. Changing it keeps the current frame,
which is what you want for hover-in / hover-out effects.
isSubframe optional [default: true]
Passed to lottie-web's setSubframe(). When true, frames are interpolated
for smooth playback. Set it to false to make the enterFrame event fire only
on whole After Effects frames, for example to trigger audio exactly on frame
boundaries (issue #71).
segments optional [default: none]
[fromFrame, toFrame] to play only part of the animation, e.g. [45, 90].
The range is applied as soon as the prop is set and re-applied whenever it
changes, so a new value starts playing that segment immediately even while the
animation is running. See Play a segment.
eventListeners optional [default: []]
This is an array of objects containing a eventName and callback function that will be registered as eventlisteners on the animation object. refer to bodymovin#events where the mention using addEventListener, for a list of available custom events. These are lottie-web animation events, not DOM events: for onMouseOver, onMouseLeave and friends put the handler on a wrapping element, as shown in Play on hover.
example:
eventListeners=[
{
eventName: 'complete',
callback: () => console.log('the animation completed:'),
},
]isClickToPauseDisabled optional [default: false]
When this prop is left unspecified or is set to false, animations are paused
or resumed when a user clicks or taps them. If you do not want this behaviour,
set this prop to true. See Click to pause for a custom
click handler.
ariaRole optional [default: button]
Role of the rendered container. Pass img for a purely decorative animation
that is described by ariaLabel.
ariaLabel optional [default: animation]
Accessible name of the container.
title optional [default: '']
title attribute (native tooltip) of the container.
style optional
Extra inline styles merged onto the container, after the width, height,
margin and layout defaults. Use it to override overflow, for example.
tabIndex optional [default: 0]
Tab order of the rendered container. Pass -1 for a purely presentational
animation that should not receive keyboard focus (issue
#130); combine it
with isClickToPauseDisabled as shown in Click to pause.
as optional [default: div]
Element or component rendered as the animation container. Use as="span" when
<Lottie /> is a child of <p>, where a div is invalid HTML (issue #73). A
custom component must forward its ref to a DOM element. See
Render inline inside a paragraph.
Hooks and alternatives
react-lottie is deliberately a thin class component over lottie-web. Its
public API is the imperative instance on a ref (play, pause,
goToAndStop, goToAndPlay, playSegments), so replacing the class with a
hooks-only component would break every existing app without changing what is
rendered. You do not need hooks to drive it declaratively, though: changing
options.animationData or options.path reloads the animation, and changes to
segments, isPaused, isStopped, speed, direction and colorFilters
are applied to the running instance. A function component can hold the state
around it (issue #117):
import React, { useState } from 'react'
import Lottie from 'react-lottie';
import animationData from './pinjump.json'
export default function Player() {
const [segment, setSegment] = useState([0, 30]);
const options = { animationData, loop: true };
return (
<div>
<Lottie options={options} segments={segment} width={240} height={240} />
<button onClick={() => setSegment([30, 60])}>next part</button>
</div>
);
}If you specifically want a hook-first API, use one of these wrappers instead:
- react-lottie-player — the
useLottiehook, withpathandanimationDatasupport (v2.1.0, last published July 2024). - lottie-react — actively maintained, hooks-based API (v3.1.2).
- @lottiefiles/react-lottie-player — LottieFiles' official wrapper, for LottieFiles-specific features (v3.6.0).
All of them wrap the same lottie-web runtime, so switching mostly changes the component API rather than how the animation is rendered.
Related Projects
- lottie-web react-lottie is a wrapper of lottie-web
- Angular Lottie angular implementation
- Vue Lottie vue implementation
- React Native Lottie react native implementation by airbnb
- IOS Lottie ios implementation by airbnb
- Android Lottie android implementation by airbnb
Contribution
Your contributions and suggestions are heartily welcome.
License
MIT
