When Do Lottie Animations Beat CSS and Video?
A Lottie animation is a JSON description of a vector timeline, usually exported from After Effects.

A Lottie animation is a JSON description of a vector timeline, usually exported from After Effects.

A Lottie animation is a JSON description of a vector timeline, usually exported from After Effects. Hernan Torrisi shipped Bodymovin in 2015; Airbnb open-sourced the players as Lottie on 1 February 2017. It is not LottieFiles the marketplace, and the classic web player still costs about 60-75 KB gzipped.
Google's head results for lottie animations are download galleries. Those galleries skip when CSS or video should win, what file size actually costs (asset plus player), how to wire two web runtimes, and where files come from.
.json, .lot, and .lottie are three different tokens.lottie-web at about 60-75 KB gzip, or dotlottie-web plus a ~500 KB WASM engine on first construct.prefers-reduced-motion does not pause Lottie. Wire matchMedia, skip autoplay, and goToAndStop the last frame.Reach for the smallest runtime that can do the job.
Job | Use | Skip Lottie if |
|---|---|---|
Hover, fade, spinner, 2-state icon | CSS / inline SVG | Player dwarfs the asset |
AE illustration, loader character, empty state | Lottie / dotLottie | You only need CSS states |
Photo, screen capture, particles AE cannot export | WebM (alpha) / MP4 | You need real footage |
Email / no JavaScript | GIF or APNG | There is no runtime |
Hover, drag, data-bound UI | A timeline is enough | |
Scroll-linked layout, React choreography | GSAP / Motion | You already have a JSON file |
CSS wins the small stuff because it already runs on the compositor thread and honors prefers-reduced-motion for free. A CSS spinner is hundreds of bytes. Shipping lottie-web for one checkmark bounce is the player bill with nothing to amortize.
Lottie wins when the motion is an After Effects illustration you refuse to rebuild in code: a branded loading character, an empty-state scene, an icon with a real timeline. You get frame-accurate playback plus play, pause, and goToAndStop on web, iOS, and Android from one file.
Video wins when the content is photographic, a screen recording, or particle work After Effects cannot export cleanly as vectors. Lottie can embed rasters. Once it does, you paid the JSON parser to babysit a movie.
On r/webdev, the highest-voted "replace the heavy motion" thread was not a Lottie win. In May 2026, u/LordVein05 deleted a 3.4 MB MP4 and rebuilt it as scripted GSAP on real DOM, under 40 KB gzipped. The line was "Not recordings, not Lottie, not Rive"; if the motion is the interface, code it.

A CSS ring is enough for an indeterminate spinner. A Lottie character is worth it when the wait is branded and the file stays vector.
Give looping loaders a pause control if they run longer than five seconds. Decorative loaders get aria-hidden="true". Do not autoplay them against reduced-motion.
Twelve timed icons can amortize the player. One bounce cannot.
If the icon is two states (default / hover), draw it in SVG and transition in CSS. If the icon is a 90-frame AE timeline the design team already signed off, export Lottie and share one player across the set.
For shader-driven, no-timeline motion (liquid, displacement, interactive WebGL), skip Lottie and look at Unicorn Studio. That is a different job.
Bodymovin is the After Effects exporter Torrisi shipped in 2015. Airbnb's 2017 libraries (Brandon Withrow, Gabriel Peal, Leland Richardson, Salih Abdul-Karim) named the format after Lotte Reiniger, who made The Adventures of Prince Achmed in 1926.
The format steward is the Lottie Animation Community under the Linux Foundation (announced 30 January 2024; JSON spec v1.0 on 17 September 2024). LottieFiles is a member, not the standards body.
Three file tokens are not synonyms:
Token | What it is | MIME | Source of record |
|---|---|---|---|
| Original Bodymovin JSON. Still valid. |
| |
| IANA extension for standard JSON Lottie |
| IANA, registered 2025-02-18 |
| LottieFiles ZIP/deflate container (assets, extras) |
|
All .lottie files are Lottie files. Not all Lottie files are .lottie. IANA's security note is the part galleries skip: documents may reference external images, and expressions can allow code execution depending on the renderer.
Bare "Lottie" in search is celebrity and cosmetics noise. Keep both words when you mean the format.
Native iOS, Android, and React Native SDKs exist from Airbnb. The rest of this page is web.
Marketing still sells "tiny JSON." You always pay two bills: the asset, and the player that parses it.
A vector loop that stays shapes and trim paths can land in tens of kilobytes. The failure mode is documented in airbnb/lottie-web#2100: a 1.2 MB designer JSON took 4-5 seconds to load, gzipped to 120 KB, and still parsed for about 3 seconds.
Gzip shrinks the wire. It does not shrink parse.
Embedded images are the other trap. On the LottieFiles forum, crushing ten PNGs saved about 400 KB on disk and only about 40 KB on the Lottie export.
The runtime still inflates every layer. r/MotionDesign threads report multi-megabyte JSON after designers already crushed the images.
LottieFiles published a worked optimize path on one file: 291.4 KB JSON to 227.8 KB (21%), then a dotLottie at 24 KB (92%), then an optimized dotLottie at 20.3 KB (93%). That path needs the dotLottie player, not classic lottie-web. The slogans rotate ("up to 80%"); the worked example is the number that holds still.
Vendor pages still say Lottie is 600% smaller than GIF. Drop that line unless you are measuring the same loop as GIF, WebM, and JSON, and unless the player is in the math.
Drop hidden and guide layers. Skip drop shadows and masks when layer plus opacity will do.
Do not key every path on every frame. Prefer .lottie only if you are actually shipping the WASM player.
Classic lottie-web (v5.13.0, MIT) did 7,720,837 weekly downloads on the live npm read for this article. GitHub issue #1184 (2018) recorded 237.5 kB minified / 60.5 kB gzip, with a light SVG build at 38.5 kB gzip.
A May 2025 community measurement put the full build at 74.8 kB gzip. The live range is about 60-75 KB gzipped. A 240 KB figure on blogs is the minified size, not gzip.
Modern @lottiefiles/dotlottie-web (v0.80.0) did 1,631,463 weekly downloads on the same read. The JS wrapper can be small (~12.1 kB gzip on v0.78).
The WASM engine is about 500 KB compressed on first construct. Call DotLottie.preload() and version-pin the preload URL. WebGL/WebGPU WASM is a separate fetch; preload() does not cover it.
@lottiefiles/lottie-player is a different package (a web component). Do not label it lottie-web.
Runtime | What you download | Use it when |
|---|---|---|
| ~60-75 KB gzip JS |
|
| Small JS + ~500 KB WASM first hit |
|
CSS / SVG | Bytes you already shipped | Hover, fade, spinner, 2-state icon |
Gzip the JavaScript and the JSON. The lottie-web README still has to say this out loud.
Call destroy() when the animation leaves the viewport. Cap concurrent complex loops.
Test mid-range Android, not a design-laptop overlay. Two heavy animations cost several times one; treat concurrency as a budget, not a default.
Two living players. A community note that "lottie-web is deprecated" is a migration pitch, not an Airbnb deprecation. lottie-web still leads npm by millions of weekly downloads.
lottie-webSVG renderer if you need DOM and CSS theming. Canvas if you need paint performance.
const anim = lottie.loadAnimation({
container: document.getElementById('lottie'),
renderer: 'svg',
loop: true,
autoplay: true,
path: '/hero.json'
});
anim.play();
anim.pause();
anim.goToAndStop(0, true);
anim.destroy();AE graph-editor bounce curves often die in Bodymovin even after baking. On r/AfterEffects (Feb 2026), u/LolaCatStevens hit that wall. In a nearby thread the same month, u/A-Kez pointed at the player: call anim.setSubframe(false) so the runtime stops interpolating away posterize FPS.
anim.setSubframe(false);If you already live in React, wrap lottie-web in one component and stop.
dotlottie-webA <canvas> plus DotLottie from @lottiefiles/dotlottie-web. Preload WASM. Pin the preload URL to the package version.
import { DotLottie } from '@lottiefiles/dotlottie-web';
const dotlottie = new DotLottie({
canvas: document.querySelector('#canvas'),
src: '/hero.lottie',
autoplay: true,
loop: true
});Use useFrameInterpolation: false when you want the same posterize behavior as setSubframe(false).
Webflow Classic Interactions still embed Lottie. If the motion belongs in components, a design engineering pass with CSS, GSAP, or Motion is the smaller runtime.
Webflow acquired GSAP on 15 October 2024. GSAP and Motion are code, not files. They do not replace Lottie for an AE illustration, and Lottie does not replace them for scroll-linked layout.
Sources is a map, not a pack roundup.
After Effects to Bodymovin. aescripts lists Bodymovin at $20. LottieFiles also ships an AE plugin (Bodymovin generation plus device preview).
Keep the AE file inside the supported subset. Expressions, Effects menu items, blending modes, luma mattes, and layer effects such as drop shadow fail or drift.
Trim paths on shapes work. Graph-editor bounce often does not.
LottieFiles.com currently claims 16 million-plus users and hundreds of thousands of animations; treat homepage totals as claims, not audited census. IconScout lists 968,000-plus Lotties on-site (a vendor count, not an audit) and has been a LottieFiles subsidiary since 10 February 2021. Disclose that if you name both.
Lordicon, Creattie, Lottielab, and useAnimations also show up in download SERPs. Pick a license you can defend, then leave.
LottieFiles Creator and Lottielab (London, founded 2021, $4M seed in 2022) are browser editors.
The format is open: MIT runtimes, LAC spec. Individual files are licensed per marketplace (free, paid, attribution).
A free player does not make a marketplace file free.
On Reddit, a recurring confusion is treating a LottieFiles subscription as the price of the format. The format has no seat fee. That file might.
Rive wins when the motion is interactive: hover, drag, game-like UI, data-bound controls. Rive was founded in 2016 by twins Guido (CEO) and Luigi Rosso. Export pricing as of 20 October 2025 starts at Cadet $9/seat/mo; the editor has a free tier; runtimes stay MIT with no runtime fee.

Lottie is an After Effects timeline you play everywhere. Rive is a native editor plus state machines plus data-bound UI.
Animade dual-exported a Gizmo mascot to Lottie and Rive (March 2026). Use both when the same character has to loop in a banner and also react in a product.
Hernan Torrisi, in October 2023, listed Lottie's structural limits: After Effects lock-in, no editor, no built-in interactivity. The same month he joined Rive. Date that list; it is not a 2026 eulogy.
The lack of an editor and its dependency on After Effects, the missing built-in interactivity, the limited possibilities with javascript, svg and canvas were some of the challenges to overcome that needed a long term strategy, focus, and of course resources.
LottieFiles Creator added state machines in late 2025. Independent evidence that they match Rive's maturity is thin. On r/AfterEffects, u/slykuiper (Sep 2025) called the Lottie state UI "messy" next to Rive setups.
Saptarshi Prakash, who first used Lottie in 2017, said the quiet part in a 2025 Creator walkthrough:
"The output plays like a video which is great to convey the idea but it's also kind of dumb. It just plays and loops and app interactions are way more than that." (Saptarshi Prakash, 2025)
Rive runtimes stay MIT, with no runtime fee. Interactions bind to real data:
Important detail about Rive: interactions like this are bound to real data. It’s not just animation, it’s a new way to build functional UI with rich motion.
If you need state machines, start with Rive. If you need an AE timeline on every platform, stay on Lottie.
CSS @media (prefers-reduced-motion: reduce) does not pause Lottie. airbnb/lottie-web#1986 asked for this in 2020. Maintainers closed it: the app, not the library, has to pick first frame, last frame, or a still.
Required pattern: read matchMedia('(prefers-reduced-motion: reduce)'), set autoplay: false, and goToAndStop the last frame so reduced-motion users see a finished still, not a blank. Listen for change. This is the same job web.dev describes for CSS, done by hand.
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
const anim = lottie.loadAnimation({
container: document.getElementById('lottie'),
renderer: 'svg',
loop: !reduce.matches,
autoplay: !reduce.matches,
path: '/loader.json'
});
function showLastFrame() {
anim.goToAndStop(anim.totalFrames - 1, true);
}
if (reduce.matches) showLastFrame();
reduce.addEventListener('change', (event) => {
if (event.matches) showLastFrame();
else anim.play();
});Pause looping loaders that run longer than five seconds. Motion triggered by interaction must be disableable.
Decorative animations get aria-hidden="true". Site-wide web accessibility still applies; Lottie does not inherit it from CSS.
iOS Low Power Mode is why Lottie still beats muted MP4 for small UI loops. WebKit 219889 is RESOLVED WONTFIX: autoplay is disabled to conserve battery, and Safari still shows a play control on muted autoplay video.
A Lottie runtime can still draw a vector loop. A muted hero video cannot.
LottieFiles on LinkedIn (2026): not all motion feels good; never add motion just for aesthetics.
Theming is an accessibility-adjacent tax. Jawish Hameed (@jawish) (Nov 2024) noted popular apps shipping duplicate Lottie files just to recolor for dark mode. One file plus CSS on an SVG renderer is cheaper than two JSONs.
prefers-reduced-motion to just work. Wire matchMedia and a last-frame fallback..lottie is a ZIP, not "the new JSON."setSubframe(false) if the preview is smoothing FPS you posterized.destroy() when the node leaves.
Vibe coding is prompting an LLM until the software runs, accepting every diff, and skipping the code. Andrej Karpathy coined the phrase in February 2025.

Chakra UI is a React component system with style props and tokens. See v3 theming, why Emotion still runs, and when shadcn or MUI is the better 2026 pick.

Framer Motion is the MIT React animation library, not the Framer canvas. Primitives, layoutId, LazyMotion sizes, and why reducedMotion defaults to never.