react-lottie 2.3.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +456 -6
- package/dist/index.js +34 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.4.0](https://github.com/chenqingspring/react-lottie/compare/v2.3.3...v2.4.0) (2026-11-08)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* apply `options.locationHref` before loading, so Safari keeps the masks, mattes, gradients and filters that lottie-web bakes into `url(#id)` references when the page uses a `<base>` tag or is served from a sub-path; changing it reloads the animation ([#111](https://github.com/chenqingspring/react-lottie/issues/111))
|
|
10
|
+
* expose the container `margin` as a prop, accepting any CSS `margin` value or a number of pixels (default `0 auto`), applied before `style` so an explicit `style.margin` still wins ([#107](https://github.com/chenqingspring/react-lottie/issues/107))
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
|
|
14
|
+
* explain how to load animation data from a URL and why a URL belongs in `options.path`, not `animationData` ([#106](https://github.com/chenqingspring/react-lottie/issues/106))
|
|
15
|
+
* explain the default click-to-pause behaviour and the `isClickToPauseDisabled` opt-out ([#110](https://github.com/chenqingspring/react-lottie/issues/110))
|
|
16
|
+
* state that the `componentWill*` lifecycles deprecated in React 16.3 and removed in React 17 are not used ([#112](https://github.com/chenqingspring/react-lottie/issues/112), [#119](https://github.com/chenqingspring/react-lottie/issues/119))
|
|
17
|
+
|
|
18
|
+
## [2.3.3](https://github.com/chenqingspring/react-lottie/compare/v2.3.2...v2.3.3) (2026-11-08)
|
|
19
|
+
|
|
20
|
+
### Documentation
|
|
21
|
+
|
|
22
|
+
* document animations that reference raster images: the `assets` array, the `assetsPath` option and rewriting assets to bundler-hashed URLs ([#49](https://github.com/chenqingspring/react-lottie/issues/49), [#59](https://github.com/chenqingspring/react-lottie/issues/59))
|
|
23
|
+
* document aliasing `lottie-web` to `lottie_light`, `lottie_light_canvas` or `lottie_light_html` to cut the bundled player by about 45% ([#79](https://github.com/chenqingspring/react-lottie/issues/79))
|
|
24
|
+
* document server-side rendering and add a regression test that renders the component in a Node process without a DOM ([#17](https://github.com/chenqingspring/react-lottie/issues/17))
|
|
25
|
+
|
|
5
26
|
## [2.3.2](https://github.com/chenqingspring/react-lottie/compare/v2.3.1...v2.3.2) (2026-11-07)
|
|
6
27
|
|
|
7
28
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -103,8 +103,17 @@ shares one React copy and tree-shakes lottie-web is smaller.
|
|
|
103
103
|
|
|
104
104
|
## Requirements
|
|
105
105
|
|
|
106
|
-
React 16.8 or newer
|
|
107
|
-
|
|
106
|
+
React 16.8 or newer, including React 17, React 18 and React 19. The
|
|
107
|
+
`react: ">=16.8.0"` peer dependency resolves without `--legacy-peer-deps` or
|
|
108
|
+
`--force`. The test suite runs against React 18; the component uses plain class
|
|
109
|
+
components and works with React 17 and React 19 as well.
|
|
110
|
+
|
|
111
|
+
The component is a class, but it only uses the lifecycles React still supports
|
|
112
|
+
(`componentDidMount`, `componentDidUpdate`, `componentWillUnmount`). The
|
|
113
|
+
`componentWill*` methods that React 16.3 deprecated and React 17 stopped
|
|
114
|
+
calling were removed in 2.0.0, so React 17 and newer do not warn about legacy
|
|
115
|
+
lifecycles or skip the data/`path` reload ([#77](https://github.com/chenqingspring/react-lottie/issues/77),
|
|
116
|
+
[#119](https://github.com/chenqingspring/react-lottie/issues/119)).
|
|
108
117
|
|
|
109
118
|
## Local development
|
|
110
119
|
|
|
@@ -185,6 +194,53 @@ belongs in `options.path`; a namespace import
|
|
|
185
194
|
(`import * as animationData`) is unwrapped automatically but the default import
|
|
186
195
|
above is preferred.
|
|
187
196
|
|
|
197
|
+
### Loading animation data from a URL
|
|
198
|
+
|
|
199
|
+
Put the URL in `options.path`, not `options.animationData`. `animationData` is
|
|
200
|
+
the parsed JSON object; lottie-web cannot fetch a string, so react-lottie throws
|
|
201
|
+
an explanatory error if you pass one (issue #106):
|
|
202
|
+
|
|
203
|
+
```jsx
|
|
204
|
+
const defaultOptions = {
|
|
205
|
+
path: 'https://someurl.com/animationData.json',
|
|
206
|
+
loop: true,
|
|
207
|
+
autoplay: true,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
<Lottie options={defaultOptions} width="1293px" height="300px" />
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`path` accepts an absolute URL, a site-root path or a relative path. lottie-web
|
|
214
|
+
fetches it after mount (nothing is requested during server rendering), and
|
|
215
|
+
changing `path` reloads the animation. Because lottie-web owns the request,
|
|
216
|
+
`colorFilters` is not applied — fetch the JSON yourself and pass the parsed
|
|
217
|
+
object as `animationData` if you need to recolor it or send request headers:
|
|
218
|
+
|
|
219
|
+
```jsx
|
|
220
|
+
import { useEffect, useState } from 'react';
|
|
221
|
+
|
|
222
|
+
function RemoteAnimation() {
|
|
223
|
+
const [animationData, setAnimationData] = useState(null);
|
|
224
|
+
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
let cancelled = false;
|
|
227
|
+
fetch('https://someurl.com/animationData.json')
|
|
228
|
+
.then((response) => response.json())
|
|
229
|
+
.then((data) => { if (!cancelled) setAnimationData(data); });
|
|
230
|
+
return () => { cancelled = true; };
|
|
231
|
+
}, []);
|
|
232
|
+
|
|
233
|
+
if (!animationData) return null;
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Lottie options={{ animationData, loop: true, autoplay: true }} height={300} />
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
See [Loading animation data without bloating the bundle](#loading-animation-data-without-bloating-the-bundle)
|
|
242
|
+
for the bundle-size trade-offs between these approaches.
|
|
243
|
+
|
|
188
244
|
### Loading animation data without bloating the bundle
|
|
189
245
|
|
|
190
246
|
A static import like `import animationData from './pinjump.json'` (the example
|
|
@@ -253,6 +309,216 @@ Finally, the file itself can usually get smaller: export at a lower precision,
|
|
|
253
309
|
remove unused layers and precompositions, avoid embedded raster images, and let
|
|
254
310
|
gzip/Brotli compress the JSON. LottieFiles' optimizer automates the first steps.
|
|
255
311
|
|
|
312
|
+
### Animations with raster images
|
|
313
|
+
|
|
314
|
+
An After Effects export that contains bitmap layers references those files from
|
|
315
|
+
the animation's `assets` array. Each entry carries a directory (`u`), a file name
|
|
316
|
+
(`p`) and an embedded flag (`e`):
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{ "id": "image_0", "w": 720, "h": 720, "u": "images/", "p": "img_0.png", "e": 0 }
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
With `"e": 0` lottie-web loads the file separately and resolves `u + p` relative
|
|
323
|
+
to the page, which breaks as soon as the app is served from a sub-path or
|
|
324
|
+
navigates client-side. Set `options.assetsPath` to the directory where the
|
|
325
|
+
images are deployed instead; react-lottie forwards unknown options to
|
|
326
|
+
lottie-web:
|
|
327
|
+
|
|
328
|
+
```jsx
|
|
329
|
+
const defaultOptions = {
|
|
330
|
+
path: '/animations/cypher.json',
|
|
331
|
+
assetsPath: '/animations/images/', // trailing slash required
|
|
332
|
+
loop: true,
|
|
333
|
+
autoplay: true,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
<Lottie options={defaultOptions} height={400} width={400} />
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Copy the images next to the JSON in `public/animations/images/` so they are
|
|
340
|
+
served as static files instead of being bundled. `assetsPath` works with both
|
|
341
|
+
`path` and `animationData`, and it applies to every image in the animation.
|
|
342
|
+
|
|
343
|
+
If you want the bundler to hash and inline the images, import them yourself and
|
|
344
|
+
rewrite the asset entries before passing the data. Don't mutate the imported
|
|
345
|
+
JSON in place when it is a frozen ESM object:
|
|
346
|
+
|
|
347
|
+
```jsx
|
|
348
|
+
import img0 from './images/img_0.png';
|
|
349
|
+
import animationData from './cypher.json';
|
|
350
|
+
|
|
351
|
+
const data = {
|
|
352
|
+
...animationData,
|
|
353
|
+
assets: animationData.assets.map((asset) => (
|
|
354
|
+
asset.id === 'image_0' ? { ...asset, u: '', p: img0 } : asset
|
|
355
|
+
)),
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
<Lottie options={{ animationData: data, loop: true, autoplay: true }} />
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
`"e": 1` means the image is embedded in the JSON as a base64 data URI. That
|
|
362
|
+
keeps the animation self-contained, but it inflates the file by roughly a third
|
|
363
|
+
and prevents the browser from caching the image separately, so prefer external
|
|
364
|
+
images for anything large.
|
|
365
|
+
|
|
366
|
+
### Shrinking the lottie-web bundle
|
|
367
|
+
|
|
368
|
+
react-lottie imports the full `lottie-web` player (299 kB minified, 611 kB
|
|
369
|
+
unminified in lottie-web 5.13), which includes the expression engine and every
|
|
370
|
+
renderer. If your animations don't use expressions and you only need one
|
|
371
|
+
renderer, alias `lottie-web` to a smaller build; react-lottie keeps working
|
|
372
|
+
because those builds expose the same `loadAnimation` API.
|
|
373
|
+
|
|
374
|
+
Vite:
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
// vite.config.js
|
|
378
|
+
export default defineConfig({
|
|
379
|
+
resolve: {
|
|
380
|
+
alias: {
|
|
381
|
+
'lottie-web': 'lottie-web/build/player/lottie_light.js',
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
webpack (Create React App, Next.js):
|
|
388
|
+
|
|
389
|
+
```js
|
|
390
|
+
// next.config.js
|
|
391
|
+
module.exports = {
|
|
392
|
+
webpack(config) {
|
|
393
|
+
config.resolve.alias['lottie-web'] = require.resolve(
|
|
394
|
+
'lottie-web/build/player/lottie_light.js'
|
|
395
|
+
);
|
|
396
|
+
return config;
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
The light builds are about 45% smaller (164 kB vs 299 kB minified):
|
|
402
|
+
|
|
403
|
+
* `lottie_light.js` — SVG renderer only, no expressions; shapes and raster
|
|
404
|
+
images still work.
|
|
405
|
+
* `lottie_light_canvas.js` — canvas renderer only, no expressions.
|
|
406
|
+
* `lottie_light_html.js` — HTML renderer only, no expressions.
|
|
407
|
+
|
|
408
|
+
Pick the build that matches `options.renderer`: `lottie_light` only registers
|
|
409
|
+
the SVG renderer, so pass `renderer: 'svg'` (the default); use
|
|
410
|
+
`lottie_light_canvas` with `renderer: 'canvas'`. Don't alias when an animation
|
|
411
|
+
uses expressions or effects, because the light builds silently skip them.
|
|
412
|
+
|
|
413
|
+
### Performance on iOS and low-end devices
|
|
414
|
+
|
|
415
|
+
File size mostly affects how long an animation takes to download and parse, not
|
|
416
|
+
its frame rate. Once playback starts, the cost of each frame comes from what the
|
|
417
|
+
renderer has to draw: the number of layers and shapes, masks and mattes,
|
|
418
|
+
expressions, effects, and the pixel size of the canvas. A multi-megabyte export
|
|
419
|
+
is a good signal that there is a lot of that (issue #133).
|
|
420
|
+
|
|
421
|
+
On iOS 14.5, Safari, Chrome and Firefox all run on WebKit, so they render at the
|
|
422
|
+
same speed; measuring in Safari is representative. Phones are also much slower
|
|
423
|
+
than a desktop, so an animation that is smooth on a laptop can drop frames on
|
|
424
|
+
mobile.
|
|
425
|
+
|
|
426
|
+
In rough order of impact:
|
|
427
|
+
|
|
428
|
+
1. **Reduce the animation.** Remove hidden and unused layers, flatten
|
|
429
|
+
precompositions, simplify masks and mattes, drop expressions and effects, and
|
|
430
|
+
avoid embedded raster images. If one scene is still heavy, split it into
|
|
431
|
+
smaller files and play only the needed range with `segments`.
|
|
432
|
+
2. **Draw it at the size it is displayed.** `width` and `height` set the
|
|
433
|
+
container size; rendering a 1200 px animation into a 300 px slot does more
|
|
434
|
+
work than necessary. With the canvas renderer, also cap the backing-store
|
|
435
|
+
resolution: at a device pixel ratio of 3, a 400 px canvas is rasterized at
|
|
436
|
+
1200 px unless you set `dpr`.
|
|
437
|
+
3. **Compare the renderers.** `svg` (the default) is usually fastest for a few
|
|
438
|
+
large shapes and stays crisp at any size. `canvas` avoids updating hundreds
|
|
439
|
+
of DOM nodes and often wins for animations with many small shapes. Measure
|
|
440
|
+
both on the target device.
|
|
441
|
+
4. **Stop off-screen animations.** lottie-web drives every playing animation
|
|
442
|
+
from the same `requestAnimationFrame` loop, so an animation scrolled out of
|
|
443
|
+
view still costs a frame. Pause it with `isPaused`, or with `pause()` on a
|
|
444
|
+
`ref`, driven by an `IntersectionObserver`.
|
|
445
|
+
|
|
446
|
+
```jsx
|
|
447
|
+
const defaultOptions = {
|
|
448
|
+
animationData,
|
|
449
|
+
loop: true,
|
|
450
|
+
autoplay: true,
|
|
451
|
+
renderer: 'canvas',
|
|
452
|
+
rendererSettings: {
|
|
453
|
+
dpr: 1, // render at CSS resolution; use 2 for a sharper middle ground
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
<Lottie options={defaultOptions} height={300} width={300} />
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
For the default `svg` renderer, `rendererSettings.progressiveLoad` builds the
|
|
461
|
+
layers over several frames instead of blocking on the first one. It does not
|
|
462
|
+
wait for the element to scroll into view; pause off-screen animations yourself:
|
|
463
|
+
|
|
464
|
+
```jsx
|
|
465
|
+
// `visible` is kept up to date by an IntersectionObserver on a wrapper element.
|
|
466
|
+
<Lottie
|
|
467
|
+
options={{ animationData, loop: true, autoplay: false, rendererSettings: { progressiveLoad: true } }}
|
|
468
|
+
isPaused={!visible}
|
|
469
|
+
/>
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
If a reduced file still drops frames, profile it: record a timeline in Safari's
|
|
473
|
+
Web Inspector while the animation plays and check whether the time goes to
|
|
474
|
+
lottie-web's frame update or to the browser painting and compositing the layers.
|
|
475
|
+
|
|
476
|
+
### Masks are missing in Safari
|
|
477
|
+
|
|
478
|
+
Safari resolves the `url(#…)` references that lottie-web generates for masks,
|
|
479
|
+
mattes, gradients and filters against the document's base URL. When the page
|
|
480
|
+
has a `<base href>` tag, or the animation is served from a path the browser
|
|
481
|
+
resolves differently, those references point at the wrong document and Safari
|
|
482
|
+
renders the animation without its masks. Chrome and Firefox usually still find
|
|
483
|
+
them.
|
|
484
|
+
|
|
485
|
+
lottie-web's documented workaround is to call `lottie.setLocationHref()` before
|
|
486
|
+
the animation is generated. react-lottie exposes it as `options.locationHref`:
|
|
487
|
+
|
|
488
|
+
```jsx
|
|
489
|
+
const defaultOptions = {
|
|
490
|
+
animationData,
|
|
491
|
+
loop: true,
|
|
492
|
+
autoplay: true,
|
|
493
|
+
locationHref: window.location.href,
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
<Lottie options={defaultOptions} height={400} width={400} />
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Pass the URL the page is actually served from, without the `#` fragment.
|
|
500
|
+
lottie-web joins the value with `#<id>` when it builds the references, so a
|
|
501
|
+
fragment produces broken URLs. The value is global to lottie-web, so pass the
|
|
502
|
+
same `locationHref` to every `<Lottie />` on the page. Changing it regenerates
|
|
503
|
+
the animation, because the base URL is baked into the generated markup.
|
|
504
|
+
|
|
505
|
+
If you already import lottie-web yourself, calling the function directly works
|
|
506
|
+
too, as long as the bundler resolves it to the same copy react-lottie uses:
|
|
507
|
+
|
|
508
|
+
```js
|
|
509
|
+
import lottie from 'lottie-web';
|
|
510
|
+
|
|
511
|
+
lottie.setLocationHref(window.location.href);
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Run that before the first animation mounts, for example at application startup.
|
|
515
|
+
|
|
516
|
+
If the masks are still missing with the correct href, the artwork is the likely
|
|
517
|
+
cause: complex masks and alpha mattes are the least reliable part of the SVG
|
|
518
|
+
renderer. Compare with the [canvas renderer](#performance-on-ios-and-low-end-devices),
|
|
519
|
+
or open the same JSON in lottie-web's own preview to confirm the problem is
|
|
520
|
+
renderer-level rather than a react-lottie bug.
|
|
521
|
+
|
|
256
522
|
### Play on hover
|
|
257
523
|
|
|
258
524
|
`eventListeners` takes lottie-web animation events (`complete`, `loopComplete`,
|
|
@@ -301,6 +567,33 @@ export default class HoverLottie extends React.Component {
|
|
|
301
567
|
|
|
302
568
|
```
|
|
303
569
|
|
|
570
|
+
### Click to pause
|
|
571
|
+
|
|
572
|
+
Clicking or tapping the animation pauses it, and clicking again resumes it.
|
|
573
|
+
This is on by default: the container also has `ariaRole="button"`, so an
|
|
574
|
+
animation that looks decorative is still an interactive control (issue
|
|
575
|
+
[#110](https://github.com/chenqingspring/react-lottie/issues/110)).
|
|
576
|
+
|
|
577
|
+
Disable it when the animation is decorative, or when you want a click to do
|
|
578
|
+
something else:
|
|
579
|
+
|
|
580
|
+
```jsx
|
|
581
|
+
<Lottie options={defaultOptions} isClickToPauseDisabled />
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
The component has no `onClick` prop. Put your handler on a wrapper element
|
|
585
|
+
instead; the click bubbles from the animation container to the wrapper:
|
|
586
|
+
|
|
587
|
+
```jsx
|
|
588
|
+
<div onClick={() => openDetails()}>
|
|
589
|
+
<Lottie options={defaultOptions} isClickToPauseDisabled />
|
|
590
|
+
</div>
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
Without `isClickToPauseDisabled` both run: playback toggles and the wrapper's
|
|
594
|
+
handler still fires after it. Leave the prop out when you want click-to-pause as
|
|
595
|
+
well.
|
|
596
|
+
|
|
304
597
|
### Play a segment
|
|
305
598
|
|
|
306
599
|
Pass `segments` to play a frame range instead of the whole animation. The
|
|
@@ -367,6 +660,102 @@ The container is a `div` by default, which is not a valid child of `<p>`. Pass
|
|
|
367
660
|
lottie-web should render into. Changing `as` reloads the animation into the new
|
|
368
661
|
element and keeps the current frame.
|
|
369
662
|
|
|
663
|
+
### Switch between several animations
|
|
664
|
+
|
|
665
|
+
Each `<Lottie>` owns one lottie-web instance, so an app can render two
|
|
666
|
+
animations side by side or keep one animation per page. To choose which one is
|
|
667
|
+
shown, keep the animation data in a map and pass the entry for the current page
|
|
668
|
+
as `options.animationData` (issue #128):
|
|
669
|
+
|
|
670
|
+
```jsx
|
|
671
|
+
import homeData from './animations/home.json';
|
|
672
|
+
import aboutData from './animations/about.json';
|
|
673
|
+
|
|
674
|
+
const animations = { home: homeData, about: aboutData };
|
|
675
|
+
|
|
676
|
+
const PageAnimation = ({ page }) => (
|
|
677
|
+
<Lottie
|
|
678
|
+
options={{
|
|
679
|
+
loop: true,
|
|
680
|
+
autoplay: true,
|
|
681
|
+
animationData: animations[page],
|
|
682
|
+
}}
|
|
683
|
+
height={300}
|
|
684
|
+
width={300}
|
|
685
|
+
/>
|
|
686
|
+
);
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
Changing `options.animationData` reloads the animation into the same container,
|
|
690
|
+
so a `key` is not required. Keep the JSON modules stable: copying the data in
|
|
691
|
+
`render` (`animationData: { ...animations[page] }`) hands the component a new
|
|
692
|
+
object on every render and reloads the animation each time. If you do want to
|
|
693
|
+
start over — to replay an intro, for example — give the component a `key` so
|
|
694
|
+
React unmounts and remounts it:
|
|
695
|
+
|
|
696
|
+
```jsx
|
|
697
|
+
<Lottie key={page} options={{ ...defaultOptions, animationData: animations[page] }} />
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Unmounting destroys the lottie-web instance, so only the visible page animates.
|
|
701
|
+
When the files are large, load them with `options.path` and fetch the JSON for
|
|
702
|
+
the next page ahead of time instead of shipping every animation in one bundle;
|
|
703
|
+
see [Loading animation data without bloating the bundle](#loading-animation-data-without-bloating-the-bundle).
|
|
704
|
+
|
|
705
|
+
### Server-side rendering
|
|
706
|
+
|
|
707
|
+
`<Lottie />` renders only a sized container element on the server; lottie-web is
|
|
708
|
+
imported but instantiated in `componentDidMount` only, so `renderToString` (and
|
|
709
|
+
Next.js/Gatsby prerendering) works and the animation starts after hydration:
|
|
710
|
+
|
|
711
|
+
```jsx
|
|
712
|
+
// server output: <div style="width:400px;height:400px;..."></div>
|
|
713
|
+
<Lottie options={defaultOptions} height={400} width={400} />
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
Importing the package outside a browser no longer throws
|
|
717
|
+
`ReferenceError: window is not defined`; lottie-web guards its DOM access at
|
|
718
|
+
module load. If your bundler still evaluates the player on the server, load the
|
|
719
|
+
component without SSR:
|
|
720
|
+
|
|
721
|
+
```jsx
|
|
722
|
+
// Next.js
|
|
723
|
+
import dynamic from 'next/dynamic';
|
|
724
|
+
|
|
725
|
+
const Lottie = dynamic(() => import('react-lottie'), { ssr: false });
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Animations loaded from `options.path` are fetched by lottie-web after mount, so
|
|
729
|
+
nothing is requested during the server render. Avoid the hydration trap
|
|
730
|
+
described in [Animations and page navigation](#animations-and-page-navigation)
|
|
731
|
+
when the container is nested in a `<p>`.
|
|
732
|
+
|
|
733
|
+
### Animations and page navigation
|
|
734
|
+
|
|
735
|
+
react-lottie creates a lottie-web instance when the component mounts and calls
|
|
736
|
+
`destroy()` on unmount, so client-side navigation is safe as long as the page
|
|
737
|
+
really unmounts. A layout that renders every page at once (or keeps pages
|
|
738
|
+
mounted when switching tabs) keeps every animation running; mount only the
|
|
739
|
+
visible one.
|
|
740
|
+
|
|
741
|
+
One non-obvious trap on SSR frameworks such as Gatsby or Next.js: the wrapper is
|
|
742
|
+
a `div` by default, and a `div` inside a `p` is invalid HTML. The browser moves
|
|
743
|
+
the `div` out of the paragraph while parsing the server-rendered page, React
|
|
744
|
+
reports a hydration mismatch (`validateDOMNesting`), and the animation node ends
|
|
745
|
+
up outside the page subtree. Navigating back and forth can then leave animation
|
|
746
|
+
nodes behind. Render the animation with `as="span"` (issue #73) or move it
|
|
747
|
+
outside the paragraph:
|
|
748
|
+
|
|
749
|
+
```jsx
|
|
750
|
+
<p>
|
|
751
|
+
Text <Lottie as="span" options={defaultOptions} /> more text
|
|
752
|
+
</p>
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
If animations still accumulate, confirm that each page unmounts (React DevTools
|
|
756
|
+
shows the component tree changing), and pause off-screen instances as described
|
|
757
|
+
in [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
|
|
758
|
+
|
|
370
759
|
### Imperative control
|
|
371
760
|
|
|
372
761
|
A `ref` to the component exposes `play()`, `pause()`, `stop()`,
|
|
@@ -398,9 +787,11 @@ the object representing the animation settings that will be instantiated by body
|
|
|
398
787
|
>
|
|
399
788
|
>**path** *optional* — a URL (or path) to a JSON animation file. Mutually exclusive with `animationData`.
|
|
400
789
|
>
|
|
401
|
-
>**renderer** *optional* [default: `svg`]
|
|
790
|
+
>**renderer** *optional* [default: `svg`] — `svg`, `canvas` or `html`. See [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
|
|
402
791
|
>
|
|
403
792
|
>**rendererSettings** *required*
|
|
793
|
+
>
|
|
794
|
+
>**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](#masks-are-missing-in-safari).
|
|
404
795
|
|
|
405
796
|
**colorFilters** *optional* [default: `[]`]
|
|
406
797
|
|
|
@@ -415,6 +806,17 @@ pixel value for containers width.
|
|
|
415
806
|
|
|
416
807
|
pixel value for containers height.
|
|
417
808
|
|
|
809
|
+
**margin** *optional* [default: `0 auto`]
|
|
810
|
+
|
|
811
|
+
Margin of the container. Accepts any CSS `margin` value (`'20px'`,
|
|
812
|
+
`'10px auto'`, `'1rem 2rem'`) or a number of pixels. Use it instead of
|
|
813
|
+
`style.margin` when the default `0 auto` misbehaves in a browser (issue #107).
|
|
814
|
+
`style` is merged after this prop, so an explicit `style.margin` still wins.
|
|
815
|
+
|
|
816
|
+
```jsx
|
|
817
|
+
<Lottie options={defaultOptions} margin="10px auto" />
|
|
818
|
+
```
|
|
819
|
+
|
|
418
820
|
**isStopped** *optional* [default: `false`]
|
|
419
821
|
|
|
420
822
|
Stops the animation and resets it to the first frame. Setting it back to `false`
|
|
@@ -464,7 +866,11 @@ eventListeners=[
|
|
|
464
866
|
```
|
|
465
867
|
|
|
466
868
|
**isClickToPauseDisabled** *optional* [default: `false`]
|
|
467
|
-
|
|
869
|
+
|
|
870
|
+
When this prop is left unspecified or is set to `false`, animations are paused
|
|
871
|
+
or resumed when a user clicks or taps them. If you do not want this behaviour,
|
|
872
|
+
set this prop to `true`. See [Click to pause](#click-to-pause) for a custom
|
|
873
|
+
click handler.
|
|
468
874
|
|
|
469
875
|
**ariaRole** *optional* [default: `button`]
|
|
470
876
|
|
|
@@ -481,8 +887,8 @@ Accessible name of the container.
|
|
|
481
887
|
|
|
482
888
|
**style** *optional*
|
|
483
889
|
|
|
484
|
-
Extra inline styles merged onto the container, after the `width`, `height
|
|
485
|
-
layout defaults. Use it to override `overflow
|
|
890
|
+
Extra inline styles merged onto the container, after the `width`, `height`,
|
|
891
|
+
`margin` and layout defaults. Use it to override `overflow`, for example.
|
|
486
892
|
|
|
487
893
|
**tabIndex** *optional* [default: `0`]
|
|
488
894
|
Tab order of the rendered container. Pass `-1` for a purely presentational
|
|
@@ -495,6 +901,50 @@ Element or component rendered as the animation container. Use `as="span"` when
|
|
|
495
901
|
custom component must forward its `ref` to a DOM element. See
|
|
496
902
|
[Render inline inside a paragraph](#render-inline-inside-a-paragraph).
|
|
497
903
|
|
|
904
|
+
## Hooks and alternatives
|
|
905
|
+
|
|
906
|
+
`react-lottie` is deliberately a thin class component over lottie-web. Its
|
|
907
|
+
public API is the imperative instance on a `ref` (`play`, `pause`,
|
|
908
|
+
`goToAndStop`, `goToAndPlay`, `playSegments`), so replacing the class with a
|
|
909
|
+
hooks-only component would break every existing app without changing what is
|
|
910
|
+
rendered. You do not need hooks to drive it declaratively, though: changing
|
|
911
|
+
`options.animationData` or `options.path` reloads the animation, and changes to
|
|
912
|
+
`segments`, `isPaused`, `isStopped`, `speed`, `direction` and `colorFilters`
|
|
913
|
+
are applied to the running instance. A function component can hold the state
|
|
914
|
+
around it (issue #117):
|
|
915
|
+
|
|
916
|
+
```jsx
|
|
917
|
+
import React, { useState } from 'react'
|
|
918
|
+
import Lottie from 'react-lottie';
|
|
919
|
+
import animationData from './pinjump.json'
|
|
920
|
+
|
|
921
|
+
export default function Player() {
|
|
922
|
+
const [segment, setSegment] = useState([0, 30]);
|
|
923
|
+
const options = { animationData, loop: true };
|
|
924
|
+
|
|
925
|
+
return (
|
|
926
|
+
<div>
|
|
927
|
+
<Lottie options={options} segments={segment} width={240} height={240} />
|
|
928
|
+
<button onClick={() => setSegment([30, 60])}>next part</button>
|
|
929
|
+
</div>
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
If you specifically want a hook-first API, use one of these wrappers instead:
|
|
935
|
+
|
|
936
|
+
* [react-lottie-player](https://github.com/mifi/react-lottie-player) — the
|
|
937
|
+
`useLottie` hook, with `path` and `animationData` support (v2.1.0, last
|
|
938
|
+
published July 2024).
|
|
939
|
+
* [lottie-react](https://github.com/Gamote/lottie-react) — actively
|
|
940
|
+
maintained, hooks-based API (v3.1.2).
|
|
941
|
+
* [@lottiefiles/react-lottie-player](https://github.com/LottieFiles/lottie-react)
|
|
942
|
+
— LottieFiles' official wrapper, for LottieFiles-specific features
|
|
943
|
+
(v3.6.0).
|
|
944
|
+
|
|
945
|
+
All of them wrap the same lottie-web runtime, so switching mostly changes the
|
|
946
|
+
component API rather than how the animation is rendered.
|
|
947
|
+
|
|
498
948
|
## Related Projects
|
|
499
949
|
|
|
500
950
|
* [lottie-web](https://github.com/airbnb/lottie-web) react-lottie is a wrapper of lottie-web
|
package/dist/index.js
CHANGED
|
@@ -100,6 +100,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
100
100
|
whether either actually changed. */
|
|
101
101
|
this.animationData = animationData;
|
|
102
102
|
this.colorFilters = colorFilters;
|
|
103
|
+
this.locationHref = options.locationHref;
|
|
103
104
|
var filteredAnimationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(animationData, options.path), colorFilters);
|
|
104
105
|
this.options = {
|
|
105
106
|
container: this.el,
|
|
@@ -117,6 +118,10 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
117
118
|
if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production' && Array.isArray(colorFilters) && colorFilters.length > 0 && !animationData) {
|
|
118
119
|
console.warn('[react-lottie] colorFilters requires options.animationData; ' + 'animations loaded from options.path are not recolored.');
|
|
119
120
|
}
|
|
121
|
+
|
|
122
|
+
/* Must happen before the animation is generated: lottie-web bakes the base
|
|
123
|
+
URL into its mask, matte, gradient and filter references (issue #111). */
|
|
124
|
+
this.setLocationHref(options);
|
|
120
125
|
this.anim = _lottieWeb["default"].loadAnimation(this.options);
|
|
121
126
|
this.registerEvents(eventListeners);
|
|
122
127
|
this.setSpeed();
|
|
@@ -151,10 +156,13 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
151
156
|
var dataChanged = this.animationData !== this.props.options.animationData;
|
|
152
157
|
var pathChanged = this.options.path !== this.props.options.path;
|
|
153
158
|
var colorFiltersChanged = !(0, _colorFilters.colorFiltersAreEqual)(this.colorFilters, this.props.colorFilters);
|
|
159
|
+
/* lottie-web bakes the base URL into each generated `url(#id)` reference,
|
|
160
|
+
so a new href only takes effect when the animation is generated again. */
|
|
161
|
+
var locationHrefChanged = this.locationHref !== this.props.options.locationHref;
|
|
154
162
|
/* The wrapper element is part of lottie-web's configuration, so a new
|
|
155
163
|
element means the animation has to be bound to the new node. */
|
|
156
164
|
var containerChanged = prevProps.as !== this.props.as;
|
|
157
|
-
if (dataChanged || pathChanged || colorFiltersChanged || containerChanged) {
|
|
165
|
+
if (dataChanged || pathChanged || colorFiltersChanged || locationHrefChanged || containerChanged) {
|
|
158
166
|
var currentFrame = this.anim ? this.anim.currentFrame : 0;
|
|
159
167
|
var wasPaused = this.anim ? this.anim.isPaused : true;
|
|
160
168
|
/* lottie-web cannot recolor a loaded animation and cannot move one to
|
|
@@ -165,11 +173,13 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
165
173
|
this.destroy();
|
|
166
174
|
this.animationData = this.props.options.animationData;
|
|
167
175
|
this.colorFilters = this.props.colorFilters;
|
|
176
|
+
this.locationHref = this.props.options.locationHref;
|
|
168
177
|
this.options = _objectSpread(_objectSpread({}, this.options), this.props.options);
|
|
169
178
|
/* `options` is caller-owned and knows nothing about the wrapper, so the
|
|
170
179
|
ref (already pointing at the newly rendered node) wins. */
|
|
171
180
|
this.options.container = this.el;
|
|
172
181
|
this.options.animationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(this.props.options.animationData, this.props.options.path), this.props.colorFilters);
|
|
182
|
+
this.setLocationHref(this.options);
|
|
173
183
|
this.anim = _lottieWeb["default"].loadAnimation(this.options);
|
|
174
184
|
this.registerEvents(this.props.eventListeners);
|
|
175
185
|
|
|
@@ -231,6 +241,22 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
231
241
|
value: function setSubframe() {
|
|
232
242
|
this.anim.setSubframe(this.props.isSubframe);
|
|
233
243
|
}
|
|
244
|
+
|
|
245
|
+
/* lottie-web writes its internal mask, matte, gradient and filter references
|
|
246
|
+
as `url(<locationHref>#id)`. Safari resolves them against the page's base
|
|
247
|
+
URL, so a `<base href>` (or a mismatched asset URL) makes it drop the
|
|
248
|
+
masks; other browsers are more forgiving. lottie-web's documented fix is
|
|
249
|
+
the global `setLocationHref()`, which has to run before the animation is
|
|
250
|
+
generated (issue #111). Passing `options.locationHref` lets callers do
|
|
251
|
+
that without importing lottie-web themselves, which also works when the
|
|
252
|
+
bundler gives them a different copy of the player. */
|
|
253
|
+
}, {
|
|
254
|
+
key: "setLocationHref",
|
|
255
|
+
value: function setLocationHref(options) {
|
|
256
|
+
if (options.locationHref != null) {
|
|
257
|
+
_lottieWeb["default"].setLocationHref(options.locationHref);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
234
260
|
}, {
|
|
235
261
|
key: "play",
|
|
236
262
|
value: function play() {
|
|
@@ -303,6 +329,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
303
329
|
var _this$props2 = this.props,
|
|
304
330
|
width = _this$props2.width,
|
|
305
331
|
height = _this$props2.height,
|
|
332
|
+
margin = _this$props2.margin,
|
|
306
333
|
ariaRole = _this$props2.ariaRole,
|
|
307
334
|
ariaLabel = _this$props2.ariaLabel,
|
|
308
335
|
isClickToPauseDisabled = _this$props2.isClickToPauseDisabled,
|
|
@@ -322,7 +349,9 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
|
|
|
322
349
|
width: getSize(width),
|
|
323
350
|
height: getSize(height),
|
|
324
351
|
overflow: 'hidden',
|
|
325
|
-
margin
|
|
352
|
+
/* `margin` is a prop so callers do not have to fight the default through
|
|
353
|
+
`style` on browsers where centring with `0 auto` misbehaves (#107). */
|
|
354
|
+
margin: getSize(margin),
|
|
326
355
|
outline: 'none'
|
|
327
356
|
}, this.props.style);
|
|
328
357
|
var onClickHandler = isClickToPauseDisabled ? function () {
|
|
@@ -357,6 +386,7 @@ Lottie.propTypes = {
|
|
|
357
386
|
})),
|
|
358
387
|
height: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]),
|
|
359
388
|
width: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]),
|
|
389
|
+
margin: _propTypes["default"].oneOfType([_propTypes["default"].string, _propTypes["default"].number]),
|
|
360
390
|
isStopped: _propTypes["default"].bool,
|
|
361
391
|
isPaused: _propTypes["default"].bool,
|
|
362
392
|
speed: _propTypes["default"].number,
|
|
@@ -383,5 +413,6 @@ Lottie.defaultProps = {
|
|
|
383
413
|
isClickToPauseDisabled: false,
|
|
384
414
|
title: '',
|
|
385
415
|
tabIndex: 0,
|
|
386
|
-
as: 'div'
|
|
416
|
+
as: 'div',
|
|
417
|
+
margin: '0 auto'
|
|
387
418
|
};
|