Rust-powered library for creating, reading, and modifying .lottie files — with native Node.js (N-API) and WebAssembly (WASM) bindings.
A .lottie file is a ZIP archive containing a manifest, Lottie JSON animations, themes, state machines, images, fonts, and audio. This library gives you a complete read/write API over that format from both Node.js and the browser.
npm install @lottiefiles/dotlottie-io
# or
yarn add @lottiefiles/dotlottie-ioThe correct prebuilt native binary for your platform is selected automatically — no build toolchain required.
Supported platforms
| Platform | Architecture |
|---|---|
| macOS | x64, arm64 (Apple Silicon) |
| Linux | x64, arm64, armv7 (glibc); x64, arm64 (musl) |
| Windows | x64, arm64 |
| Browser | WebAssembly |
npm install @lottiefiles/dotlottie-ioTwo pre-built bundles are included — pick whichever fits your setup:
| Bundle | File | Use when |
|---|---|---|
| ESM | @lottiefiles/dotlottie-io/web |
Vite, webpack, Rollup, or any modern bundler |
| IIFE | release/browser/dotlottie-io.iife.js |
Vanilla <script> tag, no build step |
See the Web API section below for usage.
| Path | Content | Notes |
|---|---|---|
manifest.json |
Package manifest | Always present |
a/{id}.json |
Lottie animation | v2; v1 used animations/{id}.json |
t/{id}.json |
Theme | v2 only |
s/{id}.json |
State machine | v2 only |
i/{filename} |
Image asset | v2; v1 used images/{filename} |
f/{filename} |
Font asset | v2 only |
u/{filename} |
Audio asset | v2; v1 used audio/{filename} |
The library reads both v1 and v2 path layouts. It always writes v2.
Prerequisites
- Rust (stable toolchain)
- Node.js ≥ 18
- Yarn
binaryen(already listed as a devDependency — provideswasm-opt)
git clone https://github.com/lottiefiles/dotlottie-io.git
cd dotlottie-io
yarn installBuild commands
# Native Node.js binding for the current platform (release)
yarn build
# Native binding, unoptimized (faster compile, useful during development)
yarn build:debug
# WebAssembly target only
yarn build:wasmyarn build:wasm runs three steps internally:
- Compiles the Rust crate to
wasm32-unknown-unknownviawasm-pack(requireswasm-pack— install withcargo install wasm-pack) - Post-processes the output with
wasm-opt --enable-bulk-memory --enable-nontrapping-float-to-int -O4(the Rust compiler emits bulk-memory and non-trapping float-to-int instructions that need these flags) - Bundles the JS glue layer with esbuild into the ESM and IIFE outputs
Other scripts
yarn test # run the test suite with ava
yarn bench # run benchmarks
yarn format # format JS, Rust, and TOML sources
yarn lint # lint with oxlintReleases are driven by Changesets and the Release workflow (.github/workflows/release.yml). Publishing is fully automated — you never run npm publish by hand.
On your feature branch, after making your changes:
yarn changelog # changeset add — pick patch/minor/major and write a summaryThis writes a file under .changeset/. Commit it alongside your code and open a PR. To check whether a branch already has one:
yarn changelog:check # changeset status --since=mainThe Release workflow runs and the Changesets action opens (or updates) a "chore: update versions" PR. That PR consumes all pending changesets — bumping the version in package.json and updating CHANGELOG.md. No packages are published yet.
Merging it triggers the publish run, which:
- Builds every target — the
buildmatrix cross-compiles all 9 native.nodebinaries (macOS, Linux glibc + musl, Windows), andbuild-wasmbuilds the browser/WASM bundle. - Assembles the packages —
napi create-npm-dirsscaffolds the per-platform packages,yarn artifactsdrops each.nodeinto its package, and the WASM bundle is restored intorelease/browser/. - Publishes —
changeset publishpublishes everything to npm, then re-publishes to GitHub Packages, and creates a GitHub Release.
You can also start a run manually from the Actions tab (workflow_dispatch).
| Package | Contents |
|---|---|
@lottiefiles/dotlottie-io |
JS loader (. → Node), WASM bundle (./web), and type definitions |
@lottiefiles/dotlottie-io-<platform> ×9 |
one prebuilt .node binary each, wired in as optionalDependencies |
All 10 packages are created automatically on first publish — there is nothing to pre-create on npm. Installing the main package pulls in only the matching platform binary via optionalDependencies.
| Secret | Used for |
|---|---|
NPMJS_TOKEN |
publishing to npm — must be an Automation token with publish rights to the @lottiefiles scope |
GITHUB_TOKEN |
GitHub Packages + GitHub Releases (provided automatically by Actions) |
const {
DotLottie,
DotLottieBuilder,
DotLottieMerger,
DotLottieReader,
MergeStrategy,
} = require('@lottiefiles/dotlottie-io')JSON payloads are passed into the API as Buffer and returned from the API as string. Use JSON.parse() directly on the returned value.
The core class representing a loaded or built .lottie package.
DotLottie.fromFile(path: string, password?: string): DotLottieLoad a .lottie file directly from a filesystem path. Pass password to open a password-protected archive.
DotLottie.fromBytes(data: Buffer, password?: string): DotLottieLoad a .lottie file from raw bytes (e.g. the result of fs.readFileSync). Pass password to decrypt a password-protected archive.
dotlottie.toBytes(password?: string): BufferSerialize the package to a .lottie ZIP Buffer. Pass password to produce an AES-256 encrypted archive. Write to disk with fs.writeFileSync.
dotlottie.addAnimation(id: string, jsonData: Buffer | string, options?: AnimationOptions): void
dotlottie.removeAnimation(id: string): void
dotlottie.getAnimationJson(id: string): string | null
dotlottie.animationIds(): string[]getAnimationJson returns null if the ID does not exist. removeAnimation throws for unknown IDs.
When a Lottie JSON contains embedded base64 assets (images, fonts, audio), addAnimation automatically extracts them into the appropriate ZIP directories and rewrites the asset references in the JSON to point to the stored files.
interface AnimationOptions {
name?: string // display name in the manifest
initialTheme?: string // ID of the theme applied by default
background?: string // background colour hint
themes?: string[] // IDs of themes associated with this animation
}dotlottie.addTheme(id: string, name: string | null, jsonData: Buffer | string): void
dotlottie.removeTheme(id: string): void
dotlottie.getThemeJson(id: string): string | null
dotlottie.themeIds(): string[]name is an optional human-readable display name. Pass null to omit it.
dotlottie.addStateMachine(id: string, name: string | null, jsonData: Buffer | string): void
dotlottie.removeStateMachine(id: string): void
dotlottie.getStateMachineJson(id: string): string | null
dotlottie.stateMachineIds(): string[]Assets (images, fonts, audio) are stored as raw binary files inside the ZIP. All three add* methods return the actual filename stored, which may differ from the requested name if a collision occurred (see Filename deduplication below).
dotlottie.addImage(filename: string, data: Buffer): string
dotlottie.getImage(filename: string): Buffer | null
dotlottie.imageFilenames(): string[]
dotlottie.addFont(filename: string, data: Buffer): string
dotlottie.getFont(filename: string): Buffer | null
dotlottie.fontFilenames(): string[]
dotlottie.addAudio(filename: string, data: Buffer): string
dotlottie.getAudio(filename: string): Buffer | null
dotlottie.audioFilenames(): string[]Only .mp3 is supported for audio. addAudio throws InvalidAudioFormat for any other extension.
When the requested filename already exists in the package, the asset is stored under a renamed key rather than throwing an error. The rename follows the pattern {stem}_{n}.{ext}, incrementing n until a free slot is found:
const first = dl.addImage('logo.png', data) // → 'logo.png'
const second = dl.addImage('logo.png', data) // → 'logo_1.png'
const third = dl.addImage('logo.png', data) // → 'logo_2.png'The returned string is the key to use with getImage / getFont / getAudio. This matches the behavior of MergeStrategy.Rename and the asset extraction that runs during addAnimation.
Cross-reference queries over the archive contents.
dotlottie.animationsUsingTheme(themeId: string): string[]Returns animation IDs whose manifest entry references the given theme (via initialTheme or the themes array).
dotlottie.animationsUsedByStateMachine(stateMachineId: string): string[]Returns all unique animation IDs referenced by PlaybackState entries in the given state machine. Returns an empty array if the state machine is not found.
dotlottie.assetReferences(animationId: string): { images: string[], fonts: string[], audio: string[] }Returns categorised external asset filenames referenced by the animation's Lottie JSON. Filenames are bare basenames (e.g. "image_0.png", "cartoon.ttf"). Returns empty arrays if the animation is not found.
dotlottie.animationsUsingAsset(filename: string): string[]Reverse lookup: returns animation IDs that reference the given asset filename in their Lottie JSON.
dotlottie.getManifestJson(): stringReturns the full manifest as a JSON string.
dotlottie.setInitial(animationId?: string | null, stateMachineId?: string | null): voidSet which animation or state machine the player loads first. Pass null to clear.
dotlottie.getInitialAnimationId(): string | nullReturns the ID of the initial animation declared in the manifest, or null if unset.
dotlottie.getInitialAnimation(): string | nullReturns the Lottie JSON source of the initial animation as a string, or null if no initial animation is set.
const { readFileSync, writeFileSync } = require('node:fs')
const { DotLottie, DotLottieBuilder } = require('@lottiefiles/dotlottie-io')
// Build a package from scratch
const builder = new DotLottieBuilder()
builder.generator('my-tool')
builder.addAnimation('hero', Buffer.from(JSON.stringify(myLottieJson)))
builder.addTheme('dark', 'Dark Theme', Buffer.from(JSON.stringify({ rules: [] })))
const dl = builder.build()
// Add assets
const storedName = dl.addImage('logo.png', readFileSync('logo.png'))
console.log(storedName) // 'logo.png' (or 'logo_1.png' if that name was taken)
dl.addAudio('click.mp3', readFileSync('click.mp3'))
// Save to disk
writeFileSync('output.lottie', dl.toBytes())
// Load back and inspect — by path or by bytes
const loaded = DotLottie.fromFile('output.lottie')
// const loaded = DotLottie.fromBytes(readFileSync('output.lottie')) // equivalent
console.log(loaded.animationIds()) // ['hero']
console.log(loaded.imageFilenames()) // ['logo.png']
console.log(loaded.audioFilenames()) // ['click.mp3']
const imgData = loaded.getImage('logo.png') // BufferA lightweight, read-only view over a .lottie archive. Only the manifest is parsed on construction — all other entries are read on demand when you call a getter. Use this instead of DotLottie when you only need to inspect or extract specific entries without loading the whole archive into memory.
open(path)— holds a live file handle via s-zip; each getter seeks directly to the requested entry. The file is never fully read into memory.fromBytes(buffer)— holds the raw buffer; each getter re-reads the ZIP central directory (~microseconds) and decompresses only the one requested entry.
DotLottieReader.open(path: string, password?: string): DotLottieReaderOpen a .lottie file from a filesystem path. Only the manifest is read; the file handle stays open for subsequent getter calls. Pass password to open a password-protected archive (file is read into memory in that case).
DotLottieReader.fromBytes(data: Buffer, password?: string): DotLottieReaderLoad from a Buffer. Only the manifest is parsed on construction. Pass password to decrypt a password-protected archive.
reader.getManifestJson(): string
reader.animationIds(): string[]
reader.themeIds(): string[]
reader.stateMachineIds(): string[]These are served directly from the eagerly-loaded manifest — no ZIP reads after construction.
Each method reads exactly one ZIP entry and returns null if the entry is not present.
reader.getAnimationJson(id: string): string | null
reader.getThemeJson(id: string): string | null
reader.getStateMachineJson(id: string): string | null
reader.getImage(filename: string): Buffer | null
reader.getFont(filename: string): Buffer | null
reader.getAudio(filename: string): Buffer | nullBoth v1 (animations/, images/, audio/) and v2 (a/, i/, u/) path layouts are supported.
reader.animationsUsingTheme(themeId: string): string[]Returns animation IDs whose manifest entry references the given theme. Reads from the manifest only — no ZIP I/O.
reader.animationsUsedByStateMachine(stateMachineId: string): string[]Returns all unique animation IDs referenced by PlaybackState entries in the given state machine. Reads one ZIP entry; throws on I/O error.
reader.assetReferences(animationId: string): { images: string[], fonts: string[], audio: string[] }Returns categorised external asset filenames referenced by the animation's Lottie JSON. Reads one ZIP entry; throws on I/O error.
reader.animationsUsingAsset(filename: string): string[]Reverse lookup: returns animation IDs that reference the given asset filename. Reads every animation ZIP entry (O(n) reads); throws on I/O error. Use intoDotLottie() first if you need to call this repeatedly.
const { readFileSync } = require('node:fs')
const { DotLottieReader } = require('@lottiefiles/dotlottie-io')
// From a file path — file handle stays open, nothing else is read yet
const reader = DotLottieReader.open('package.lottie')
console.log(reader.animationIds()) // from manifest, free
console.log(reader.stateMachineIds()) // from manifest, free
// Only this entry is decompressed and returned
const animJson = reader.getAnimationJson('hero')
if (animJson) {
console.log(JSON.parse(animJson).nm)
}
// From a buffer — useful when you already have the bytes in memory
const reader2 = DotLottieReader.fromBytes(readFileSync('package.lottie'))
const font = reader2.getFont('MyFont.ttf') // Buffer | nullA stateful builder for constructing .lottie packages. Configure it with the methods below, then call build() to produce a DotLottie.
new DotLottieBuilder()builder.version(v: string): voidSet the manifest version string.
builder.generator(g: string): voidSet the generator field in the manifest (e.g. your tool name and version).
builder.mergeStrategy(strategy: MergeStrategy): voidCollision strategy when source animations share IDs. Defaults to MergeStrategy.Rename. See MergeStrategy for values.
builder.initialAnimation(id: string): void
builder.initialStateMachine(id: string): voidSet which animation or state machine the player loads first.
builder.addAnimation(id: string, jsonData: Buffer | string, options?: AnimationOptions): void
builder.addTheme(id: string, name: string | null, jsonData: Buffer | string): void
builder.addStateMachine(id: string, name: string | null, jsonData: Buffer | string): void
builder.addImage(filename: string, data: Buffer): void
builder.addFont(filename: string, data: Buffer): void
builder.addAudio(filename: string, data: Buffer): voidThe builder queues all additions and resolves filename collisions at build() time using the same deduplication logic as the direct API (see Filename deduplication).
builder.build(): DotLottieConsumes the builder configuration and returns a DotLottie instance. Throws if any content is invalid (e.g. a non-MP3 audio file).
const { DotLottieBuilder } = require('@lottiefiles/dotlottie-io')
const builder = new DotLottieBuilder()
builder.generator('my-tool v1.0')
builder.initialAnimation('intro')
builder.addAnimation('intro', Buffer.from(JSON.stringify(introJson)))
builder.addAnimation('hero', Buffer.from(JSON.stringify(heroJson)))
builder.addAudio('click.mp3', clickMp3Buffer)
const dl = builder.build()
console.log(dl.animationIds()) // ['intro', 'hero']
console.log(dl.audioFilenames()) // ['click.mp3']An exported enum used by both DotLottieMerger and DotLottieBuilder.mergeStrategy().
const { MergeStrategy } = require('@lottiefiles/dotlottie-io')
MergeStrategy.Rename // appends _1, _2, … on collision (default)
MergeStrategy.Skip // silently drops the incoming item on collision
MergeStrategy.Fail // throws on any collisionMerges one or more .lottie packages into a base package, with configurable collision handling.
new DotLottieMerger(strategy?: MergeStrategy) // defaults to MergeStrategy.RenameCollision strategies
| Strategy | Animations / Themes / State Machines | Assets (images, fonts, audio) |
|---|---|---|
MergeStrategy.Rename |
Appends _1, _2, … to the incoming ID |
Same — renames the incoming file |
MergeStrategy.Skip |
Silently drops the incoming item | Silently drops the incoming file |
MergeStrategy.Fail |
Throws on any collision | Throws on any collision |
Asset path references inside animation JSON are automatically updated to point to renamed files.
merger.merge(base: DotLottie, others: DotLottie[]): DotLottieMerges all packages in others into base and returns the combined result.
const { DotLottieBuilder, DotLottieMerger, MergeStrategy } = require('@lottiefiles/dotlottie-io')
function build(id) {
const b = new DotLottieBuilder()
b.addAnimation(id, Buffer.from(JSON.stringify(lottieJson)))
return b.build()
}
const merger = new DotLottieMerger(MergeStrategy.Rename)
const result = merger.merge(build('hero'), [build('hero'), build('outro')])
console.log(result.animationIds()) // ['hero', 'hero_1', 'outro']Add dotlottie-io to your Cargo.toml (it re-exports all the input/option types):
[dependencies]
dotlottie-io = "0.1"Enable the native-fs feature for streaming file I/O. With it, to_file and from_file use a streaming ZIP implementation that keeps peak RAM usage around 5 MB regardless of archive size, and DotLottieReader::open becomes available.
[dependencies]
dotlottie-io = { version = "0.1", features = ["native-fs"] }The core struct representing a loaded or built .lottie archive.
DotLottie::from_file(path: impl AsRef<Path>, password: Option<&str>) -> Result<Self, DotLottieError>Load a .lottie archive from disk. Pass Some("password") to open a password-protected archive. Without native-fs, the whole file is read into memory; with native-fs and no password, streaming reads are used.
DotLottie::from_bytes(bytes: &[u8], password: Option<&str>) -> Result<Self, DotLottieError>Parse a .lottie archive from a byte slice.
dotlottie.to_file(path: impl AsRef<Path>, password: Option<&str>) -> Result<(), DotLottieError>Write the archive to a file. With native-fs enabled and no password, uses streaming compression (low peak RAM). Otherwise uses ZipWriter written directly to a File. Pass Some("password") to produce an AES-256 encrypted archive.
dotlottie.to_bytes(password: Option<&str>) -> Result<Vec<u8>, DotLottieError>Serialize the archive to a byte buffer. Pass Some("password") to produce an AES-256 encrypted archive.
dotlottie.add_animation(anim: LottieAnimation, opts: Option<AnimationOptions>) -> Result<(), DotLottieError>
dotlottie.remove_animation(id: &str) -> Result<(), DotLottieError>
dotlottie.get_animation(id: &str) -> Option<&LottieAnimation>
dotlottie.animation_ids() -> Vec<&str>When a LottieAnimation contains embedded base64 assets, add_animation extracts them into the appropriate ZIP directories and rewrites asset references in the JSON.
dotlottie.add_theme(id: impl Into<String>, name: Option<String>, theme: Theme) -> Result<(), DotLottieError>
dotlottie.remove_theme(id: &str) -> Result<(), DotLottieError>
dotlottie.get_theme(id: &str) -> Option<&Theme>
dotlottie.theme_ids() -> Vec<&str>dotlottie.add_state_machine(id: impl Into<String>, name: Option<String>, sm: StateMachineDefinition) -> Result<(), DotLottieError>
dotlottie.remove_state_machine(id: &str) -> Result<(), DotLottieError>
dotlottie.get_state_machine(id: &str) -> Option<&StateMachineDefinition>
dotlottie.state_machine_ids() -> Vec<&str>All add_* methods return the actual filename stored, which may differ from the requested name if a collision was resolved by appending a numeric suffix (e.g. logo_1.png).
dotlottie.add_image(filename: impl Into<String>, data: Vec<u8>) -> Result<String, DotLottieError>
dotlottie.get_image(filename: &str) -> Option<&[u8]>
dotlottie.image_filenames() -> Vec<&str>
dotlottie.add_font(filename: impl Into<String>, data: Vec<u8>) -> Result<String, DotLottieError>
dotlottie.get_font(filename: &str) -> Option<&[u8]>
dotlottie.font_filenames() -> Vec<&str>
dotlottie.add_audio(filename: impl Into<String>, data: Vec<u8>) -> Result<String, DotLottieError>
dotlottie.get_audio(filename: &str) -> Option<&[u8]>
dotlottie.audio_filenames() -> Vec<&str>Only .mp3 is supported for audio. add_audio returns Err(InvalidAudioFormat) for any other extension.
dotlottie.animations_using_theme(theme_id: &str) -> Vec<String>
dotlottie.animations_used_by_state_machine(state_machine_id: &str) -> Vec<String>
dotlottie.asset_references(animation_id: &str) -> AssetReferences // { images, fonts, audio }
dotlottie.animations_using_asset(filename: &str) -> Vec<String>dotlottie.manifest() -> &Manifest
dotlottie.set_initial(animation_id: Option<String>, state_machine_id: Option<String>) -> Result<(), DotLottieError>
dotlottie.initial_animation_id() -> Option<String>
dotlottie.initial_animation_json() -> Option<String>use dotlottie_io::{DotLottie, DotLottieBuilder};
use dotlottie_io::AnimationSource;
use std::path::PathBuf;
// Load an existing archive
let dl = DotLottie::from_file("input.lottie", None)?;
println!("{:?}", dl.animation_ids());
// Build a new archive from a Lottie JSON file
let dl = DotLottieBuilder::new()
.generator("my-tool")
.add_animation(
AnimationSource::JsonFile { id: "hero".into(), path: PathBuf::from("hero.json") },
None,
)
.build()?;
// Write to disk — to_file is always available, streaming when native-fs is enabled
dl.to_file("output.lottie", None)?;
// Round-trip with password protection
dl.to_file("protected.lottie", Some("s3cr3t"))?;
let loaded = DotLottie::from_file("protected.lottie", Some("s3cr3t"))?;A lightweight lazy view over a .lottie archive. Only manifest.json is parsed on construction; all other entries are read on demand.
// Requires the `native-fs` feature — file handle stays open, getters seek directly to each entry
DotLottieReader::open(path: impl AsRef<Path>, password: Option<&str>) -> Result<Self, DotLottieError>
// Always available — holds the buffer; getters re-parse the central directory on each call
DotLottieReader::from_bytes(bytes: Vec<u8>, password: Option<&str>) -> Result<Self, DotLottieError>reader.manifest // pub field: &Manifest
reader.initial_animation_id() -> Option<String>
reader.animations_using_theme(theme_id: &str) -> Vec<String>Each method reads exactly one ZIP entry and returns Ok(None) if the entry is not present.
reader.animation(id: &str) -> Result<Option<LottieAnimation>, DotLottieError>
reader.theme(id: &str) -> Result<Option<Theme>, DotLottieError>
reader.state_machine(id: &str) -> Result<Option<StateMachineDefinition>, DotLottieError>
reader.image(filename: &str) -> Result<Option<Vec<u8>>, DotLottieError>
reader.font(filename: &str) -> Result<Option<Vec<u8>>, DotLottieError>
reader.audio(filename: &str) -> Result<Option<Vec<u8>>, DotLottieError>
reader.initial_animation_json() -> Result<Option<String>, DotLottieError>reader.animations_used_by_state_machine(state_machine_id: &str) -> Result<Vec<String>, DotLottieError>
reader.asset_references(animation_id: &str) -> Result<AssetReferences, DotLottieError>
reader.animations_using_asset(filename: &str) -> Result<Vec<String>, DotLottieError> // O(n) ZIP readsreader.into_dotlottie() -> Result<DotLottie, DotLottieError>Eagerly loads the full archive into a DotLottie when mutation or full access is needed.
A builder for constructing .lottie archives from various sources.
DotLottieBuilder::new() -> Selfbuilder.version(v: impl Into<String>) -> Self
builder.generator(g: impl Into<String>) -> Self
builder.initial_animation(id: impl Into<String>) -> Self
builder.initial_state_machine(id: impl Into<String>) -> Self
builder.merge_strategy(strategy: MergeStrategy) -> Selfbuilder.add_animation(src: AnimationSource, opts: Option<AnimationOptions>) -> Self
builder.add_theme(id: impl Into<String>, name: Option<String>, src: ThemeSource) -> Self
builder.add_state_machine(id: impl Into<String>, name: Option<String>, src: StateMachineSource) -> Self
builder.add_image(src: ImageSource) -> Self
builder.add_font(src: FontSource) -> Self
builder.add_audio(src: AudioSource) -> SelfAnimationSource variants: JsonBytes { id, data }, JsonFile { id, path }, LottieFile { path }. The LottieFile variant merges an entire .lottie archive using the configured MergeStrategy.
builder.build() -> Result<DotLottie, DotLottieError>Merges one or more .lottie archives into a base archive with configurable collision handling. See MergeStrategy for values.
DotLottieMerger::new(strategy: MergeStrategy) -> Self
merger.merge(base: DotLottie, others: impl IntoIterator<Item = DotLottie>) -> Result<DotLottie, DotLottieError>use dotlottie_io::MergeStrategy;
MergeStrategy::RenameOnCollision // appends _1, _2, … (default)
MergeStrategy::SkipOnCollision // silently drops the incoming item
MergeStrategy::FailOnCollision // returns Err on any collisionThe API surface is identical to Node.js — the same classes (DotLottie, DotLottieBuilder, DotLottieMerger, DotLottieReader) with the same methods and signatures. The WASM module must be initialized once before the classes are usable.
DotLottieReader is available in the browser via DotLottieReader.fromBytes(uint8Array). The open(path) method is not available in WASM as there is no filesystem.
import { init } from '@lottiefiles/dotlottie-io/web'
const { DotLottie, DotLottieBuilder, DotLottieMerger } = await init()The bundler automatically copies dotlottie-io.wasm to your output directory and resolves the URL via import.meta.url — no extra configuration needed.
<script src="node_modules/dotlottie-io/release/browser/dotlottie-io.iife.js"></script>
<script>
;(async () => {
const { DotLottie, DotLottieBuilder, DotLottieMerger } = await DotLottieIO.init()
// classes are ready
})()
</script>dotlottie-io.wasm must be served from the same directory as the IIFE script. Pass an explicit URL to override:
const { DotLottie } = await DotLottieIO.init('/assets/dotlottie-io.wasm')The wasm-bindgen runtime manages WASM memory automatically. Pass Uint8Array values directly — no manual allocation needed:
const { DotLottie } = await init()
// Load from a FileReader result
const dotlottie = DotLottie.fromBytes(new Uint8Array(fileReaderResult))
// Add an animation from a fetched buffer
const json = await fetch('/animations/hero.json').then((r) => r.arrayBuffer())
dotlottie.addAnimation('hero', new Uint8Array(json))See example/index.html for a complete interactive .lottie editor running in the browser. It supports:
- Drag-and-drop loading of
.lottiepackages - Adding animations (
.json), themes, state machines, images, and audio (.mp3) - An Assets tab showing image thumbnails, live font previews, and audio players for all embedded assets
- Export to
.lottie
To run it, start a local HTTP server from the repo root:
npx serve .
# then open http://localhost:3000/example/All I/O methods accept an optional password parameter. When provided, all ZIP entries (including manifest.json) are encrypted with AES-256.
const { DotLottie, DotLottieReader } = require('@lottiefiles/dotlottie-io')
const { writeFileSync, readFileSync } = require('fs')
// Write an encrypted archive
const dl = DotLottie.fromFile('source.lottie')
const encrypted = dl.toBytes('my-secret-password')
writeFileSync('protected.lottie', encrypted)
// Read an encrypted archive
const loaded = DotLottie.fromBytes(readFileSync('protected.lottie'), 'my-secret-password')
console.log(loaded.animationIds())
// Lazy inspection via DotLottieReader
const reader = DotLottieReader.open('protected.lottie', 'my-secret-password')
console.log(reader.animationIds())use dotlottie_io::{DotLottie, DotLottieReader};
// Write an encrypted archive — to_file and to_bytes both accept a password
let dl = DotLottie::from_file("source.lottie", None)?;
dl.to_file("protected.lottie", Some("my-secret-password"))?;
// or: let bytes = dl.to_bytes(Some("my-secret-password"))?;
// Read an encrypted archive
let loaded = DotLottie::from_file("protected.lottie", Some("my-secret-password"))?;
println!("{:?}", loaded.animation_ids());
// Lazy inspection via DotLottieReader (requires `native-fs` feature for open)
let reader = DotLottieReader::open("protected.lottie", Some("my-secret-password"))?;
println!("{:?}", reader.manifest.animations);
// Or from bytes (always available)
let bytes = std::fs::read("protected.lottie")?;
let reader = DotLottieReader::from_bytes(bytes, Some("my-secret-password"))?;"password required to open this archive"— the archive is encrypted but no password was provided."wrong password or corrupted archive"— the password is incorrect or the archive is corrupt.
MIT