react-lottie 2.3.2 → 2.3.3
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 +8 -0
- package/README.md +265 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
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.3.3](https://github.com/chenqingspring/react-lottie/compare/v2.3.2...v2.3.3) (2026-11-08)
|
|
6
|
+
|
|
7
|
+
### Documentation
|
|
8
|
+
|
|
9
|
+
* 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))
|
|
10
|
+
* 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))
|
|
11
|
+
* 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))
|
|
12
|
+
|
|
5
13
|
## [2.3.2](https://github.com/chenqingspring/react-lottie/compare/v2.3.1...v2.3.2) (2026-11-07)
|
|
6
14
|
|
|
7
15
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -103,8 +103,10 @@ 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.
|
|
108
110
|
|
|
109
111
|
## Local development
|
|
110
112
|
|
|
@@ -253,6 +255,170 @@ Finally, the file itself can usually get smaller: export at a lower precision,
|
|
|
253
255
|
remove unused layers and precompositions, avoid embedded raster images, and let
|
|
254
256
|
gzip/Brotli compress the JSON. LottieFiles' optimizer automates the first steps.
|
|
255
257
|
|
|
258
|
+
### Animations with raster images
|
|
259
|
+
|
|
260
|
+
An After Effects export that contains bitmap layers references those files from
|
|
261
|
+
the animation's `assets` array. Each entry carries a directory (`u`), a file name
|
|
262
|
+
(`p`) and an embedded flag (`e`):
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{ "id": "image_0", "w": 720, "h": 720, "u": "images/", "p": "img_0.png", "e": 0 }
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
With `"e": 0` lottie-web loads the file separately and resolves `u + p` relative
|
|
269
|
+
to the page, which breaks as soon as the app is served from a sub-path or
|
|
270
|
+
navigates client-side. Set `options.assetsPath` to the directory where the
|
|
271
|
+
images are deployed instead; react-lottie forwards unknown options to
|
|
272
|
+
lottie-web:
|
|
273
|
+
|
|
274
|
+
```jsx
|
|
275
|
+
const defaultOptions = {
|
|
276
|
+
path: '/animations/cypher.json',
|
|
277
|
+
assetsPath: '/animations/images/', // trailing slash required
|
|
278
|
+
loop: true,
|
|
279
|
+
autoplay: true,
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
<Lottie options={defaultOptions} height={400} width={400} />
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Copy the images next to the JSON in `public/animations/images/` so they are
|
|
286
|
+
served as static files instead of being bundled. `assetsPath` works with both
|
|
287
|
+
`path` and `animationData`, and it applies to every image in the animation.
|
|
288
|
+
|
|
289
|
+
If you want the bundler to hash and inline the images, import them yourself and
|
|
290
|
+
rewrite the asset entries before passing the data. Don't mutate the imported
|
|
291
|
+
JSON in place when it is a frozen ESM object:
|
|
292
|
+
|
|
293
|
+
```jsx
|
|
294
|
+
import img0 from './images/img_0.png';
|
|
295
|
+
import animationData from './cypher.json';
|
|
296
|
+
|
|
297
|
+
const data = {
|
|
298
|
+
...animationData,
|
|
299
|
+
assets: animationData.assets.map((asset) => (
|
|
300
|
+
asset.id === 'image_0' ? { ...asset, u: '', p: img0 } : asset
|
|
301
|
+
)),
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
<Lottie options={{ animationData: data, loop: true, autoplay: true }} />
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`"e": 1` means the image is embedded in the JSON as a base64 data URI. That
|
|
308
|
+
keeps the animation self-contained, but it inflates the file by roughly a third
|
|
309
|
+
and prevents the browser from caching the image separately, so prefer external
|
|
310
|
+
images for anything large.
|
|
311
|
+
|
|
312
|
+
### Shrinking the lottie-web bundle
|
|
313
|
+
|
|
314
|
+
react-lottie imports the full `lottie-web` player (299 kB minified, 611 kB
|
|
315
|
+
unminified in lottie-web 5.13), which includes the expression engine and every
|
|
316
|
+
renderer. If your animations don't use expressions and you only need one
|
|
317
|
+
renderer, alias `lottie-web` to a smaller build; react-lottie keeps working
|
|
318
|
+
because those builds expose the same `loadAnimation` API.
|
|
319
|
+
|
|
320
|
+
Vite:
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
// vite.config.js
|
|
324
|
+
export default defineConfig({
|
|
325
|
+
resolve: {
|
|
326
|
+
alias: {
|
|
327
|
+
'lottie-web': 'lottie-web/build/player/lottie_light.js',
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
webpack (Create React App, Next.js):
|
|
334
|
+
|
|
335
|
+
```js
|
|
336
|
+
// next.config.js
|
|
337
|
+
module.exports = {
|
|
338
|
+
webpack(config) {
|
|
339
|
+
config.resolve.alias['lottie-web'] = require.resolve(
|
|
340
|
+
'lottie-web/build/player/lottie_light.js'
|
|
341
|
+
);
|
|
342
|
+
return config;
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
The light builds are about 45% smaller (164 kB vs 299 kB minified):
|
|
348
|
+
|
|
349
|
+
* `lottie_light.js` — SVG renderer only, no expressions; shapes and raster
|
|
350
|
+
images still work.
|
|
351
|
+
* `lottie_light_canvas.js` — canvas renderer only, no expressions.
|
|
352
|
+
* `lottie_light_html.js` — HTML renderer only, no expressions.
|
|
353
|
+
|
|
354
|
+
Pick the build that matches `options.renderer`: `lottie_light` only registers
|
|
355
|
+
the SVG renderer, so pass `renderer: 'svg'` (the default); use
|
|
356
|
+
`lottie_light_canvas` with `renderer: 'canvas'`. Don't alias when an animation
|
|
357
|
+
uses expressions or effects, because the light builds silently skip them.
|
|
358
|
+
|
|
359
|
+
### Performance on iOS and low-end devices
|
|
360
|
+
|
|
361
|
+
File size mostly affects how long an animation takes to download and parse, not
|
|
362
|
+
its frame rate. Once playback starts, the cost of each frame comes from what the
|
|
363
|
+
renderer has to draw: the number of layers and shapes, masks and mattes,
|
|
364
|
+
expressions, effects, and the pixel size of the canvas. A multi-megabyte export
|
|
365
|
+
is a good signal that there is a lot of that (issue #133).
|
|
366
|
+
|
|
367
|
+
On iOS 14.5, Safari, Chrome and Firefox all run on WebKit, so they render at the
|
|
368
|
+
same speed; measuring in Safari is representative. Phones are also much slower
|
|
369
|
+
than a desktop, so an animation that is smooth on a laptop can drop frames on
|
|
370
|
+
mobile.
|
|
371
|
+
|
|
372
|
+
In rough order of impact:
|
|
373
|
+
|
|
374
|
+
1. **Reduce the animation.** Remove hidden and unused layers, flatten
|
|
375
|
+
precompositions, simplify masks and mattes, drop expressions and effects, and
|
|
376
|
+
avoid embedded raster images. If one scene is still heavy, split it into
|
|
377
|
+
smaller files and play only the needed range with `segments`.
|
|
378
|
+
2. **Draw it at the size it is displayed.** `width` and `height` set the
|
|
379
|
+
container size; rendering a 1200 px animation into a 300 px slot does more
|
|
380
|
+
work than necessary. With the canvas renderer, also cap the backing-store
|
|
381
|
+
resolution: at a device pixel ratio of 3, a 400 px canvas is rasterized at
|
|
382
|
+
1200 px unless you set `dpr`.
|
|
383
|
+
3. **Compare the renderers.** `svg` (the default) is usually fastest for a few
|
|
384
|
+
large shapes and stays crisp at any size. `canvas` avoids updating hundreds
|
|
385
|
+
of DOM nodes and often wins for animations with many small shapes. Measure
|
|
386
|
+
both on the target device.
|
|
387
|
+
4. **Stop off-screen animations.** lottie-web drives every playing animation
|
|
388
|
+
from the same `requestAnimationFrame` loop, so an animation scrolled out of
|
|
389
|
+
view still costs a frame. Pause it with `isPaused`, or with `pause()` on a
|
|
390
|
+
`ref`, driven by an `IntersectionObserver`.
|
|
391
|
+
|
|
392
|
+
```jsx
|
|
393
|
+
const defaultOptions = {
|
|
394
|
+
animationData,
|
|
395
|
+
loop: true,
|
|
396
|
+
autoplay: true,
|
|
397
|
+
renderer: 'canvas',
|
|
398
|
+
rendererSettings: {
|
|
399
|
+
dpr: 1, // render at CSS resolution; use 2 for a sharper middle ground
|
|
400
|
+
},
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
<Lottie options={defaultOptions} height={300} width={300} />
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
For the default `svg` renderer, `rendererSettings.progressiveLoad` builds the
|
|
407
|
+
layers over several frames instead of blocking on the first one. It does not
|
|
408
|
+
wait for the element to scroll into view; pause off-screen animations yourself:
|
|
409
|
+
|
|
410
|
+
```jsx
|
|
411
|
+
// `visible` is kept up to date by an IntersectionObserver on a wrapper element.
|
|
412
|
+
<Lottie
|
|
413
|
+
options={{ animationData, loop: true, autoplay: false, rendererSettings: { progressiveLoad: true } }}
|
|
414
|
+
isPaused={!visible}
|
|
415
|
+
/>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
If a reduced file still drops frames, profile it: record a timeline in Safari's
|
|
419
|
+
Web Inspector while the animation plays and check whether the time goes to
|
|
420
|
+
lottie-web's frame update or to the browser painting and compositing the layers.
|
|
421
|
+
|
|
256
422
|
### Play on hover
|
|
257
423
|
|
|
258
424
|
`eventListeners` takes lottie-web animation events (`complete`, `loopComplete`,
|
|
@@ -367,6 +533,102 @@ The container is a `div` by default, which is not a valid child of `<p>`. Pass
|
|
|
367
533
|
lottie-web should render into. Changing `as` reloads the animation into the new
|
|
368
534
|
element and keeps the current frame.
|
|
369
535
|
|
|
536
|
+
### Switch between several animations
|
|
537
|
+
|
|
538
|
+
Each `<Lottie>` owns one lottie-web instance, so an app can render two
|
|
539
|
+
animations side by side or keep one animation per page. To choose which one is
|
|
540
|
+
shown, keep the animation data in a map and pass the entry for the current page
|
|
541
|
+
as `options.animationData` (issue #128):
|
|
542
|
+
|
|
543
|
+
```jsx
|
|
544
|
+
import homeData from './animations/home.json';
|
|
545
|
+
import aboutData from './animations/about.json';
|
|
546
|
+
|
|
547
|
+
const animations = { home: homeData, about: aboutData };
|
|
548
|
+
|
|
549
|
+
const PageAnimation = ({ page }) => (
|
|
550
|
+
<Lottie
|
|
551
|
+
options={{
|
|
552
|
+
loop: true,
|
|
553
|
+
autoplay: true,
|
|
554
|
+
animationData: animations[page],
|
|
555
|
+
}}
|
|
556
|
+
height={300}
|
|
557
|
+
width={300}
|
|
558
|
+
/>
|
|
559
|
+
);
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Changing `options.animationData` reloads the animation into the same container,
|
|
563
|
+
so a `key` is not required. Keep the JSON modules stable: copying the data in
|
|
564
|
+
`render` (`animationData: { ...animations[page] }`) hands the component a new
|
|
565
|
+
object on every render and reloads the animation each time. If you do want to
|
|
566
|
+
start over — to replay an intro, for example — give the component a `key` so
|
|
567
|
+
React unmounts and remounts it:
|
|
568
|
+
|
|
569
|
+
```jsx
|
|
570
|
+
<Lottie key={page} options={{ ...defaultOptions, animationData: animations[page] }} />
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Unmounting destroys the lottie-web instance, so only the visible page animates.
|
|
574
|
+
When the files are large, load them with `options.path` and fetch the JSON for
|
|
575
|
+
the next page ahead of time instead of shipping every animation in one bundle;
|
|
576
|
+
see [Loading animation data without bloating the bundle](#loading-animation-data-without-bloating-the-bundle).
|
|
577
|
+
|
|
578
|
+
### Server-side rendering
|
|
579
|
+
|
|
580
|
+
`<Lottie />` renders only a sized container element on the server; lottie-web is
|
|
581
|
+
imported but instantiated in `componentDidMount` only, so `renderToString` (and
|
|
582
|
+
Next.js/Gatsby prerendering) works and the animation starts after hydration:
|
|
583
|
+
|
|
584
|
+
```jsx
|
|
585
|
+
// server output: <div style="width:400px;height:400px;..."></div>
|
|
586
|
+
<Lottie options={defaultOptions} height={400} width={400} />
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Importing the package outside a browser no longer throws
|
|
590
|
+
`ReferenceError: window is not defined`; lottie-web guards its DOM access at
|
|
591
|
+
module load. If your bundler still evaluates the player on the server, load the
|
|
592
|
+
component without SSR:
|
|
593
|
+
|
|
594
|
+
```jsx
|
|
595
|
+
// Next.js
|
|
596
|
+
import dynamic from 'next/dynamic';
|
|
597
|
+
|
|
598
|
+
const Lottie = dynamic(() => import('react-lottie'), { ssr: false });
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
Animations loaded from `options.path` are fetched by lottie-web after mount, so
|
|
602
|
+
nothing is requested during the server render. Avoid the hydration trap
|
|
603
|
+
described in [Animations and page navigation](#animations-and-page-navigation)
|
|
604
|
+
when the container is nested in a `<p>`.
|
|
605
|
+
|
|
606
|
+
### Animations and page navigation
|
|
607
|
+
|
|
608
|
+
react-lottie creates a lottie-web instance when the component mounts and calls
|
|
609
|
+
`destroy()` on unmount, so client-side navigation is safe as long as the page
|
|
610
|
+
really unmounts. A layout that renders every page at once (or keeps pages
|
|
611
|
+
mounted when switching tabs) keeps every animation running; mount only the
|
|
612
|
+
visible one.
|
|
613
|
+
|
|
614
|
+
One non-obvious trap on SSR frameworks such as Gatsby or Next.js: the wrapper is
|
|
615
|
+
a `div` by default, and a `div` inside a `p` is invalid HTML. The browser moves
|
|
616
|
+
the `div` out of the paragraph while parsing the server-rendered page, React
|
|
617
|
+
reports a hydration mismatch (`validateDOMNesting`), and the animation node ends
|
|
618
|
+
up outside the page subtree. Navigating back and forth can then leave animation
|
|
619
|
+
nodes behind. Render the animation with `as="span"` (issue #73) or move it
|
|
620
|
+
outside the paragraph:
|
|
621
|
+
|
|
622
|
+
```jsx
|
|
623
|
+
<p>
|
|
624
|
+
Text <Lottie as="span" options={defaultOptions} /> more text
|
|
625
|
+
</p>
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
If animations still accumulate, confirm that each page unmounts (React DevTools
|
|
629
|
+
shows the component tree changing), and pause off-screen instances as described
|
|
630
|
+
in [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
|
|
631
|
+
|
|
370
632
|
### Imperative control
|
|
371
633
|
|
|
372
634
|
A `ref` to the component exposes `play()`, `pause()`, `stop()`,
|
|
@@ -398,7 +660,7 @@ the object representing the animation settings that will be instantiated by body
|
|
|
398
660
|
>
|
|
399
661
|
>**path** *optional* — a URL (or path) to a JSON animation file. Mutually exclusive with `animationData`.
|
|
400
662
|
>
|
|
401
|
-
>**renderer** *optional* [default: `svg`]
|
|
663
|
+
>**renderer** *optional* [default: `svg`] — `svg`, `canvas` or `html`. See [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
|
|
402
664
|
>
|
|
403
665
|
>**rendererSettings** *required*
|
|
404
666
|
|