react-lottie 2.2.0 → 2.3.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,12 @@
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.0](https://github.com/chenqingspring/react-lottie/compare/v2.2.0...v2.3.0) (2026-11-05)
6
+
7
+ ### Features
8
+
9
+ * add an `as` prop to choose the element rendered around the animation, so `<Lottie as="span" />` can be nested in a `<p>` without invalid HTML. Changing `as` rebinds the animation to the new node and keeps the current frame ([#73](https://github.com/chenqingspring/react-lottie/issues/73))
10
+
5
11
  ## [2.2.0](https://github.com/chenqingspring/react-lottie/compare/v2.1.0...v2.2.0) (2026-11-05)
6
12
 
7
13
  ### Features
package/README.md CHANGED
@@ -116,6 +116,74 @@ export default class LottieControl extends React.Component {
116
116
 
117
117
  ```
118
118
 
119
+ ### Loading animation data without bloating the bundle
120
+
121
+ A static import like `import animationData from './pinjump.json'` (the example
122
+ above) makes the bundler inline the animation into the JavaScript chunk that
123
+ imports it. A 500 kB JSON file then becomes 500 kB of JavaScript the browser has
124
+ to download and parse before the page can run. The JSON is data, not code, so
125
+ keep it out of the main bundle:
126
+
127
+ **Serve it from `public/` and use `options.path`.** In Create React App, Vite
128
+ and Next.js, files in `public/` are copied to the build output as static
129
+ assets; they are not bundled into JavaScript. `lottie-web` fetches the file when
130
+ the component mounts, and the browser caches it separately from the app bundle:
131
+
132
+ ```jsx
133
+ const defaultOptions = {
134
+ path: '/animations/pinjump.json', // relative to the deployed site root
135
+ loop: true,
136
+ autoplay: true,
137
+ rendererSettings: {
138
+ preserveAspectRatio: 'xMidYMid slice'
139
+ }
140
+ };
141
+
142
+ <Lottie options={defaultOptions} height={400} width={400} />
143
+ ```
144
+
145
+ Because lottie-web owns the fetch, `colorFilters` is not applied to animations
146
+ loaded this way — fetch the JSON yourself if you need to recolor it.
147
+
148
+ **Or load it on demand with a dynamic `import()`.** If you need the parsed data
149
+ in your own code, a dynamic import puts the JSON in a separate chunk that is
150
+ downloaded only when the component renders:
151
+
152
+ ```jsx
153
+ import { useEffect, useState } from 'react';
154
+ import Lottie from 'react-lottie';
155
+
156
+ export default function Animation() {
157
+ const [animationData, setAnimationData] = useState(null);
158
+
159
+ useEffect(() => {
160
+ let cancelled = false;
161
+ import('./pinjump.json').then(({ default: data }) => {
162
+ if (!cancelled) setAnimationData(data);
163
+ });
164
+ return () => { cancelled = true; };
165
+ }, []);
166
+
167
+ if (!animationData) return null;
168
+
169
+ return (
170
+ <Lottie
171
+ options={{ animationData, loop: true, autoplay: true }}
172
+ height={400}
173
+ width={400}
174
+ />
175
+ );
176
+ }
177
+ ```
178
+
179
+ Rendering `<Animation />` lazily (for example when it scrolls into view) defers
180
+ even that chunk until it is needed. The same idea works with `React.lazy` around
181
+ a component that imports the JSON.
182
+
183
+ Finally, the file itself can usually get smaller: export at a lower precision,
184
+ remove unused layers and precompositions, avoid embedded raster images, and let
185
+ gzip/Brotli compress the JSON. LottieFiles' optimizer automates the first steps.
186
+
119
187
  ### Play on hover
120
188
 
121
189
  `eventListeners` takes lottie-web animation events (`complete`, `loopComplete`,
@@ -215,6 +283,21 @@ frame and paused/playing state are preserved. Animations loaded from
215
283
  `options.path` are not recolored, since the JSON is fetched by lottie-web:
216
284
  load the JSON yourself and pass it as `animationData` instead.
217
285
 
286
+ ### Render inline inside a paragraph
287
+
288
+ The container is a `div` by default, which is not a valid child of `<p>`. Pass
289
+ `as` to choose the element (issue #73):
290
+
291
+ ```jsx
292
+ <p>
293
+ Text <Lottie as="span" options={defaultOptions} ariaRole="img" /> more text
294
+ </p>
295
+ ```
296
+
297
+ `as` accepts a tag name or a component that forwards its `ref` to the DOM node
298
+ lottie-web should render into. Changing `as` reloads the animation into the new
299
+ element and keeps the current frame.
300
+
218
301
  ### Imperative control
219
302
 
220
303
  A `ref` to the component exposes `play()`, `pause()`, `stop()`,
@@ -336,6 +419,13 @@ layout defaults. Use it to override `overflow` or `margin`.
336
419
  Tab order of the rendered container. Pass `-1` for a purely presentational
337
420
  animation that should not receive keyboard focus.
338
421
 
422
+ **as** *optional* [default: `div`]
423
+
424
+ Element or component rendered as the animation container. Use `as="span"` when
425
+ `<Lottie />` is a child of `<p>`, where a `div` is invalid HTML (issue #73). A
426
+ custom component must forward its `ref` to a DOM element. See
427
+ [Render inline inside a paragraph](#render-inline-inside-a-paragraph).
428
+
339
429
  ## Related Projects
340
430
 
341
431
  * [lottie-web](https://github.com/airbnb/lottie-web) react-lottie is a wrapper of lottie-web
package/dist/index.js CHANGED
@@ -122,24 +122,31 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
122
122
  var dataChanged = this.animationData !== this.props.options.animationData;
123
123
  var pathChanged = this.options.path !== this.props.options.path;
124
124
  var colorFiltersChanged = !(0, _colorFilters.colorFiltersAreEqual)(this.colorFilters, this.props.colorFilters);
125
- if (dataChanged || pathChanged || colorFiltersChanged) {
125
+ /* The wrapper element is part of lottie-web's configuration, so a new
126
+ element means the animation has to be bound to the new node. */
127
+ var containerChanged = prevProps.as !== this.props.as;
128
+ if (dataChanged || pathChanged || colorFiltersChanged || containerChanged) {
126
129
  var currentFrame = this.anim ? this.anim.currentFrame : 0;
127
130
  var wasPaused = this.anim ? this.anim.isPaused : true;
128
- /* lottie-web cannot recolor a loaded animation, so a filter change has to
129
- load the data again. Only a filter-only change restores the frame. */
130
- var colorOnlyChange = colorFiltersChanged && !dataChanged && !pathChanged;
131
+ /* lottie-web cannot recolor a loaded animation and cannot move one to
132
+ another node, so those changes load the same data again. Only then is
133
+ it worth restoring the frame and playback state. */
134
+ var reloadInPlace = !dataChanged && !pathChanged;
131
135
  this.deRegisterEvents(prevProps.eventListeners);
132
136
  this.destroy();
133
137
  this.animationData = this.props.options.animationData;
134
138
  this.colorFilters = this.props.colorFilters;
135
139
  this.options = _objectSpread(_objectSpread({}, this.options), this.props.options);
140
+ /* `options` is caller-owned and knows nothing about the wrapper, so the
141
+ ref (already pointing at the newly rendered node) wins. */
142
+ this.options.container = this.el;
136
143
  this.options.animationData = (0, _colorFilters.applyColorFilters)(this.props.options.animationData, this.props.colorFilters);
137
144
  this.anim = _lottieWeb["default"].loadAnimation(this.options);
138
145
  this.registerEvents(this.props.eventListeners);
139
146
 
140
- /* Keep the frame and playback state so a theme change does not restart
141
- a paused or half-played animation. */
142
- if (colorOnlyChange) {
147
+ /* Keep the frame and playback state so a theme or element change does
148
+ not restart a paused or half-played animation. */
149
+ if (reloadInPlace) {
143
150
  if (this.props.segments) this.anim.playSegments(this.props.segments, true);
144
151
  if (Number.isFinite(currentFrame)) this.anim.goToAndStop(currentFrame, true);
145
152
  if (!wasPaused) this.anim.play();
@@ -264,7 +271,8 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
264
271
  ariaLabel = _this$props2.ariaLabel,
265
272
  isClickToPauseDisabled = _this$props2.isClickToPauseDisabled,
266
273
  title = _this$props2.title,
267
- tabIndex = _this$props2.tabIndex;
274
+ tabIndex = _this$props2.tabIndex,
275
+ Container = _this$props2.as;
268
276
  var getSize = function getSize(initial) {
269
277
  var size;
270
278
  if (typeof initial === 'number') {
@@ -289,7 +297,7 @@ var Lottie = exports["default"] = /*#__PURE__*/function (_React$Component) {
289
297
  // Click-to-pause is intentional and role="button" is set; keyboard control
290
298
  // is not part of the API yet.
291
299
  // eslint-disable-next-line jsx-a11y/click-events-have-key-events
292
- _react["default"].createElement("div", {
300
+ _react["default"].createElement(Container, {
293
301
  ref: function ref(c) {
294
302
  _this4.el = c;
295
303
  },
@@ -324,7 +332,8 @@ Lottie.propTypes = {
324
332
  isClickToPauseDisabled: _propTypes["default"].bool,
325
333
  title: _propTypes["default"].string,
326
334
  style: _propTypes["default"].object,
327
- tabIndex: _propTypes["default"].number
335
+ tabIndex: _propTypes["default"].number,
336
+ as: _propTypes["default"].elementType
328
337
  };
329
338
  Lottie.defaultProps = {
330
339
  eventListeners: [],
@@ -337,5 +346,6 @@ Lottie.defaultProps = {
337
346
  ariaLabel: 'animation',
338
347
  isClickToPauseDisabled: false,
339
348
  title: '',
340
- tabIndex: 0
349
+ tabIndex: 0,
350
+ as: 'div'
341
351
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-lottie",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "funding": "https://opencollective.com/react-lottie",
5
5
  "description": "lottie animation view for React",
6
6
  "repository": {