react-lottie 2.3.3 → 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 CHANGED
@@ -2,6 +2,19 @@
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
+
5
18
  ## [2.3.3](https://github.com/chenqingspring/react-lottie/compare/v2.3.2...v2.3.3) (2026-11-08)
6
19
 
7
20
  ### Documentation
package/README.md CHANGED
@@ -108,6 +108,13 @@ React 16.8 or newer, including React 17, React 18 and React 19. The
108
108
  `--force`. The test suite runs against React 18; the component uses plain class
109
109
  components and works with React 17 and React 19 as well.
110
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)).
117
+
111
118
  ## Local development
112
119
 
113
120
  Run the Storybook stories with `npm start` (port 9010) and the test suite with
@@ -187,6 +194,53 @@ belongs in `options.path`; a namespace import
187
194
  (`import * as animationData`) is unwrapped automatically but the default import
188
195
  above is preferred.
189
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
+
190
244
  ### Loading animation data without bloating the bundle
191
245
 
192
246
  A static import like `import animationData from './pinjump.json'` (the example
@@ -419,6 +473,52 @@ If a reduced file still drops frames, profile it: record a timeline in Safari's
419
473
  Web Inspector while the animation plays and check whether the time goes to
420
474
  lottie-web's frame update or to the browser painting and compositing the layers.
421
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
+
422
522
  ### Play on hover
423
523
 
424
524
  `eventListeners` takes lottie-web animation events (`complete`, `loopComplete`,
@@ -467,6 +567,33 @@ export default class HoverLottie extends React.Component {
467
567
 
468
568
  ```
469
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
+
470
597
  ### Play a segment
471
598
 
472
599
  Pass `segments` to play a frame range instead of the whole animation. The
@@ -663,6 +790,8 @@ the object representing the animation settings that will be instantiated by body
663
790
  >**renderer** *optional* [default: `svg`] — `svg`, `canvas` or `html`. See [Performance on iOS and low-end devices](#performance-on-ios-and-low-end-devices).
664
791
  >
665
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).
666
795
 
667
796
  **colorFilters** *optional* [default: `[]`]
668
797
 
@@ -677,6 +806,17 @@ pixel value for containers width.
677
806
 
678
807
  pixel value for containers height.
679
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
+
680
820
  **isStopped** *optional* [default: `false`]
681
821
 
682
822
  Stops the animation and resets it to the first frame. Setting it back to `false`
@@ -726,7 +866,11 @@ eventListeners=[
726
866
  ```
727
867
 
728
868
  **isClickToPauseDisabled** *optional* [default: `false`]
729
- When this props 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`.
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.
730
874
 
731
875
  **ariaRole** *optional* [default: `button`]
732
876
 
@@ -743,8 +887,8 @@ Accessible name of the container.
743
887
 
744
888
  **style** *optional*
745
889
 
746
- Extra inline styles merged onto the container, after the `width`, `height` and
747
- layout defaults. Use it to override `overflow` or `margin`.
890
+ Extra inline styles merged onto the container, after the `width`, `height`,
891
+ `margin` and layout defaults. Use it to override `overflow`, for example.
748
892
 
749
893
  **tabIndex** *optional* [default: `0`]
750
894
  Tab order of the rendered container. Pass `-1` for a purely presentational
@@ -757,6 +901,50 @@ Element or component rendered as the animation container. Use `as="span"` when
757
901
  custom component must forward its `ref` to a DOM element. See
758
902
  [Render inline inside a paragraph](#render-inline-inside-a-paragraph).
759
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
+
760
948
  ## Related Projects
761
949
 
762
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: '0 auto',
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
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-lottie",
3
- "version": "2.3.3",
3
+ "version": "2.4.0",
4
4
  "funding": "https://opencollective.com/react-lottie",
5
5
  "description": "lottie animation view for React",
6
6
  "repository": {