react-lottie 2.3.0 → 2.3.2

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,20 @@
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.2](https://github.com/chenqingspring/react-lottie/compare/v2.3.1...v2.3.2) (2026-11-07)
6
+
7
+ ### Bug Fixes
8
+
9
+ * accept a namespace import (`import * as animationData`) by unwrapping its `default` export and clone frozen animation objects before handing them to lottie-web, so they no longer fail with `Cannot read property 'length' of undefined` (#24, #65)
10
+ * throw an actionable error when `options.animationData` is a string or when neither `animationData` nor `path` is provided, instead of lottie-web's `Cannot create property 'tf' on string` / `Cannot read property 'length' of undefined` deep in the renderer (#44)
11
+ * fix the README examples that read `.default` from a default import, which passed `undefined` as the animation data
12
+
13
+ ## [2.3.1](https://github.com/chenqingspring/react-lottie/compare/v2.3.0...v2.3.1) (2026-11-05)
14
+
15
+ ### Bug Fixes
16
+
17
+ * tolerate a missing or non-array `eventListeners` prop instead of throwing `eventListeners.forEach is not a function` on mount and unmount, which could break animations rendered through `React.lazy` ([#137](https://github.com/chenqingspring/react-lottie/issues/137))
18
+
5
19
  ## [2.3.0](https://github.com/chenqingspring/react-lottie/compare/v2.2.0...v2.3.0) (2026-11-05)
6
20
 
7
21
  ### Features
package/README.md CHANGED
@@ -37,6 +37,70 @@ npm install --save react-lottie
37
37
  > [GitHub](https://github.com/chenqingspring/react-lottie/issues) and Relay
38
38
  > will review and fix it.
39
39
 
40
+ ## Using from a CDN (no bundler)
41
+
42
+ The published `dist/index.js` is a CommonJS build for bundlers and npm
43
+ consumers. It uses `exports` and expects `react`, `prop-types` and `lottie-web`
44
+ to be resolvable, so loading that file with a plain `<script>` tag fails with
45
+ `Uncaught ReferenceError: exports is not defined`. The package does not ship a
46
+ UMD/browser global build.
47
+
48
+ To use react-lottie without a build step, load it as an ES module from a CDN
49
+ and map `react` to the same copy the page uses. [esm.sh](https://esm.sh)
50
+ supports `?external=react` for exactly this:
51
+
52
+ ```html
53
+ <script type="importmap">
54
+ {
55
+ "imports": {
56
+ "react": "https://esm.sh/react@18",
57
+ "react-dom/client": "https://esm.sh/react-dom@18/client",
58
+ "react-lottie": "https://esm.sh/react-lottie@2?external=react"
59
+ }
60
+ }
61
+ </script>
62
+ <script type="module">
63
+ import React from 'react';
64
+ import { createRoot } from 'react-dom/client';
65
+ import Lottie from 'react-lottie';
66
+
67
+ const options = {
68
+ path: '/animations/pinjump.json',
69
+ loop: true,
70
+ autoplay: true,
71
+ };
72
+
73
+ createRoot(document.getElementById('root')).render(
74
+ React.createElement(Lottie, { options, height: 400, width: 400 })
75
+ );
76
+ </script>
77
+ ```
78
+
79
+ Without a compiler for JSX, create elements with `React.createElement`, or add
80
+ `htm`/Babel. Keeping `react` in the import map gives the page one React
81
+ instance; a CDN build that bundles its own React (such as jsDelivr's `+esm`
82
+ output) produces two copies, which breaks context and hooks.
83
+
84
+ If you only need to play an animation and not the React wrapper, load
85
+ lottie-web directly and skip React entirely:
86
+
87
+ ```html
88
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js"></script>
89
+ <script>
90
+ lottie.loadAnimation({
91
+ container: document.getElementById('root'),
92
+ path: '/animations/pinjump.json',
93
+ renderer: 'svg',
94
+ loop: true,
95
+ autoplay: true,
96
+ });
97
+ </script>
98
+ ```
99
+
100
+ An ESM CDN build still downloads React, React DOM, lottie-web and react-lottie,
101
+ so it removes the build step but not the bytes. For production, a bundler that
102
+ shares one React copy and tree-shakes lottie-web is smaller.
103
+
40
104
  ## Requirements
41
105
 
42
106
  React 16.8 or newer. The test suite runs against React 18; the component uses
@@ -95,7 +159,7 @@ export default class LottieControl extends React.Component {
95
159
  const defaultOptions = {
96
160
  loop: true,
97
161
  autoplay: true,
98
- animationData: animationData.default,
162
+ animationData,
99
163
  rendererSettings: {
100
164
  preserveAspectRatio: 'xMidYMid slice'
101
165
  }
@@ -116,6 +180,11 @@ export default class LottieControl extends React.Component {
116
180
 
117
181
  ```
118
182
 
183
+ Pass the parsed animation object as `options.animationData`. A file path or URL
184
+ belongs in `options.path`; a namespace import
185
+ (`import * as animationData`) is unwrapped automatically but the default import
186
+ above is preferred.
187
+
119
188
  ### Loading animation data without bloating the bundle
120
189
 
121
190
  A static import like `import animationData from './pinjump.json'` (the example
@@ -211,7 +280,7 @@ export default class HoverLottie extends React.Component {
211
280
  const defaultOptions = {
212
281
  loop: false,
213
282
  autoplay: false,
214
- animationData: animationData.default,
283
+ animationData,
215
284
  rendererSettings: {
216
285
  preserveAspectRatio: 'xMidYMid slice'
217
286
  }
package/dist/index.js CHANGED
@@ -32,6 +32,35 @@ var segmentsAreEqual = function segmentsAreEqual(a, b) {
32
32
  return value === b[index];
33
33
  });
34
34
  };
35
+
36
+ /* lottie-web crashes deep inside its renderer with "Cannot read property
37
+ 'length' of undefined" when the data is not a plain animation object. Accept
38
+ the shapes users actually pass (a namespace import, a frozen ESM object) and
39
+ fail with an actionable message for the rest (#24, #44, #65). */
40
+ var _normalizeAnimationData = function normalizeAnimationData(animationData, path) {
41
+ if (animationData === undefined || animationData === null) {
42
+ if (!path) {
43
+ throw new Error('[react-lottie] options must include `animationData` (the imported JSON) ' + 'or `path` (a URL to the JSON file).');
44
+ }
45
+ return animationData;
46
+ }
47
+ if (typeof animationData === 'string') {
48
+ throw new Error('[react-lottie] options.animationData must be an object, not a string. ' + 'Pass a file path or URL as options.path instead, or call JSON.parse() ' + 'on JSON text first.');
49
+ }
50
+
51
+ /* `import * as animationData` wraps the payload in a `default` property,
52
+ which lottie-web cannot read (#24, #65). */
53
+ if (!Array.isArray(animationData.layers) && animationData["default"]) {
54
+ return _normalizeAnimationData(animationData["default"], path);
55
+ }
56
+
57
+ /* lottie-web annotates the loaded animation (`__complete`), which throws on
58
+ the frozen objects produced by strict ESM bundlers (#24). */
59
+ if (Object.isFrozen(animationData)) {
60
+ return JSON.parse(JSON.stringify(animationData));
61
+ }
62
+ return animationData;
63
+ };
35
64
  var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
36
65
  function Lottie() {
37
66
  var _this;
@@ -71,7 +100,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
71
100
  whether either actually changed. */
72
101
  this.animationData = animationData;
73
102
  this.colorFilters = colorFilters;
74
- var filteredAnimationData = (0, _colorFilters.applyColorFilters)(animationData, colorFilters);
103
+ var filteredAnimationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(animationData, options.path), colorFilters);
75
104
  this.options = {
76
105
  container: this.el,
77
106
  renderer: renderer,
@@ -140,7 +169,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
140
169
  /* `options` is caller-owned and knows nothing about the wrapper, so the
141
170
  ref (already pointing at the newly rendered node) wins. */
142
171
  this.options.container = this.el;
143
- this.options.animationData = (0, _colorFilters.applyColorFilters)(this.props.options.animationData, this.props.colorFilters);
172
+ this.options.animationData = (0, _colorFilters.applyColorFilters)(_normalizeAnimationData(this.props.options.animationData, this.props.options.path), this.props.colorFilters);
144
173
  this.anim = _lottieWeb["default"].loadAnimation(this.options);
145
174
  this.registerEvents(this.props.eventListeners);
146
175
 
@@ -179,7 +208,8 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
179
208
  value: function componentWillUnmount() {
180
209
  this.deRegisterEvents(this.props.eventListeners);
181
210
  this.destroy();
182
- this.options.animationData = null;
211
+ /* A failed loadAnimation (e.g. invalid animationData) leaves no options. */
212
+ if (this.options) this.options.animationData = null;
183
213
  this.anim = null;
184
214
  }
185
215
  }, {
@@ -248,6 +278,11 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
248
278
  key: "registerEvents",
249
279
  value: function registerEvents(eventListeners) {
250
280
  var _this2 = this;
281
+ /* `eventListeners` is array-valued and defaults to `[]`, but a wrapper
282
+ component (React.lazy, an HOC, a prop spread with `null`) can hand us
283
+ something else. Treat anything that is not an array as "no listeners";
284
+ blindly calling forEach used to crash on mount and unmount (#137). */
285
+ if (!this.anim || !Array.isArray(eventListeners)) return;
251
286
  eventListeners.forEach(function (eventListener) {
252
287
  _this2.anim.addEventListener(eventListener.eventName, eventListener.callback);
253
288
  });
@@ -256,6 +291,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
256
291
  key: "deRegisterEvents",
257
292
  value: function deRegisterEvents(eventListeners) {
258
293
  var _this3 = this;
294
+ if (!this.anim || !Array.isArray(eventListeners)) return;
259
295
  eventListeners.forEach(function (eventListener) {
260
296
  _this3.anim.removeEventListener(eventListener.eventName, eventListener.callback);
261
297
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-lottie",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "funding": "https://opencollective.com/react-lottie",
5
5
  "description": "lottie animation view for React",
6
6
  "repository": {