Migrating from dotlottie-js
A code-for-code guide to moving a project from dotlottie-js to dotlottie-io, covering API differences, package changes, and what's new.
Migrating from dotlottie-js
This guide is for teams with an existing dotlottie-js integration who want to move to dotlottie-io.
Why migrate
dotlottie-io does the same job as dotlottie-js — creating, reading, and manipulating .lottie files — with a Rust core exposed through Node.js and WebAssembly bindings. Compared to dotlottie-js, it offers:
A fully synchronous API for local file operations (no
Promise-basedbuild()step)A lazy reader (
DotLottieReader) for inspecting large archives without loading everything into memoryPassword-protected, AES-256 encrypted archives
Cross-reference query methods (which animations use this theme/asset/state machine)
A shared Rust core with other parts of the dotLottie ecosystem
dotlottie-io will supersede dotlottie-js, so it's best to migrate earlier rather than later.
1. Package name change
This is the easiest thing to miss: the npm scope changes.
- npm install @dotlottie/dotlottie-js
+ npm install @lottiefiles/dotlottie-io- import { DotLottie } from '@dotlottie/dotlottie-js'
+ const { DotLottie } = require('@lottiefiles/dotlottie-io')dotlottie-io's Node.js package is CommonJS-first (require), though it also works with import under Node's interop. The browser build is ESM-only and requires an explicit init() call — see Platform Notes.
2. At a glance
| Aspect | dotlottie-js | dotlottie-io |
| Core | Pure JavaScript/TypeScript | Rust, with Node (N-API) and WASM bindings |
| API style | Async, single fluent DotLottie class | Sync, split across DotLottie / DotLottieBuilder / DotLottieReader / DotLottieMerger |
| Build step | Required: await dotlottie.build() before export | Not required — DotLottieBuilder.build() is the construction step itself, and is synchronous |
| URL fetching | Built in (addAnimation({ url })) | Not built in — fetch yourself, then pass the bytes |
| Browser download | Built in (.download(filename)) | Not built in — build a Blob from toBytes() yourself |
| Lazy/low-memory reading | ❌ | ✅ DotLottieReader |
| Password protection | ❌ | ✅ AES-256, via a password argument on I/O methods |
| Cross-reference queries | ❌ | ✅ animationsUsingTheme, animationsUsedByStateMachine, assetReferences, animationsUsingAsset |
| Merge collision handling | merge() throws on any duplicate ID | DotLottieMerger with a chosen MergeStrategy (Rename/Skip/Fail) |
| Standalone asset attachment | Not exposed on DotLottie — assets come from animation JSON only | ✅ addImage/addFont/addAudio as first-class methods on DotLottie |
| Font assets | Extracted from animation JSON only | Same, plus explicit addFont/getFont/fontFilenames |
3. Code-for-code examples
Creating and saving a file
dotlottie-js:
import { DotLottie } from "@dotlottie/dotlottie-js";
const dotlottie = new DotLottie();
dotlottie.addAnimation({ id: "hero", data: heroLottieJson });
await dotlottie.build();
const buffer = await dotlottie.toArrayBuffer();
await writeFile("output.lottie", Buffer.from(buffer));dotlottie-io:
const { DotLottieBuilder } = require("@lottiefiles/dotlottie-io");
const { writeFileSync } = require("node:fs");
const builder = new DotLottieBuilder();
builder.addAnimation("hero", JSON.stringify(heroLottieJson));
const dl = builder.build(); // synchronous
writeFileSync("output.lottie", dl.toBytes()); // synchronousNote the different construction pattern: dotlottie-js mutates a new DotLottie() in place and finalizes it with build(); dotlottie-io queues content on a DotLottieBuilder and build() produces a DotLottie.
Loading and inspecting a file
dotlottie-js:
import { DotLottie } from "@dotlottie/dotlottie-js";
const dotlottie = await new DotLottie().fromArrayBuffer(arrayBuffer);
console.log(dotlottie.animations.length);dotlottie-io:
const { DotLottie } = require("@lottiefiles/dotlottie-io");
const dl = DotLottie.fromBytes(buffer); // synchronous, no await
console.log(dl.animationIds().length);Adding a theme
dotlottie-js:
dotlottie.addTheme({
id: "dark",
data: { colors: { primary: "#FFFFFF" } },
});dotlottie-io:
dl.addTheme("dark", "Dark Theme", JSON.stringify({ rules: [] }));Note the theme data shape itself differs too — dotlottie-io (and dotLottie V2 generally) expects a { rules: [...] } structure, not the ad hoc { colors: {...} } shape some dotlottie-js examples use. Check your theme JSON against the V2 theming spec if you're carrying data over directly.
Merging files
dotlottie-js:
const merged = dl1.merge(dl2, dl3); // throws on any duplicate ID
await merged.build();dotlottie-io:
const { DotLottieMerger, MergeStrategy } = require("@lottiefiles/dotlottie-io");
const merger = new DotLottieMerger(MergeStrategy.Rename); // choose collision behavior
const merged = merger.merge(dl1, [dl2, dl3]); // synchronous, ready immediately4. What doesn't carry over directly
addAnimation({ url: '...' })—dotlottie-jsfetches the URL duringbuild().dotlottie-iohas no network layer at all: fetch the JSON yourself (fetch(url).then(r => r.arrayBuffer())or Node'shttp/https), then calladdAnimation(id, buffer)..download(filename)—dotlottie-iodoesn't touch the DOM. In the browser, build the download yourself from the bytestoBytes()returns:const bytes = dl.toBytes(); const blob = new Blob([bytes], { type: "application/zip" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "output.lottie"; a.click(); URL.revokeObjectURL(url);Author/description/keywords metadata — these were
dotlottie-jsv1 (pre-1.0.0) fields (setAuthor,setDescription,setKeywords,setRevision). They were already dropped fromdotlottie-js's own v2 manifest schema, so if you're migrating from adotlottie-jsv2 project this doesn't change anything. If you're still on pre-1.0.0dotlottie-js, see its own migration guide first for the v1→v2 manifest changes, since the same fields are absent fromdotlottie-io's manifest too.Playback fields on animations (
autoplay,loop,speed,direction,hover,intermission,themeColor) — also already dropped indotlottie-jsv2'sAnimationOptionsin favor of player-side configuration;dotlottie-io'sAnimationOptionsmatches this (see Managing Animations).
5. New capabilities worth adopting
Once the port is functionally complete, consider using what dotlottie-io adds:
DotLottieReaderfor any code path that only needs to inspect a.lottiefile, not load and re-serialize it — see How to Read a .lottie File.Password protection for any
.lottiefiles distributed outside your control — see How to Password-Protect a .lottie File.Cross-reference queries for build-time linting (unused assets, orphaned themes) — see How to Query Relationships.
General recommendations
Update the package name and import style first, then run your existing test suite against the new synchronous API shape — most failures will be
awaits that need removing or restructuring, not logic changes.Replace any
addAnimation({ url })usage with an explicit fetch step.Replace any
.download()usage with theBlob/anchor pattern shown above.If you use themes, verify their JSON shape matches the
{ rules: [...] }structuredotlottie-ioexpects.
Next up: API Reference