relottie Usage Guide
A comprehensive guide to using relottie packages. Learn about prerequisites, installation, the basic processing pipeline, and how to use plugins for Lottie animations.
Guide: Using relottie
This guide builds upon the Quick Start and dives deeper into setting up your environment and using the core relottie processor programmatically.
Prerequisites
Node.js: relottie requires a maintained version of Node.js. As noted in the source documentation, versions 16.0+ and 18.0+ are tested and recommended.
Package Manager:
npmoryarnfor installing packages.TypeScript (Recommended): The relottie ecosystem is fully typed with TypeScript. While you can use it with JavaScript, using TypeScript provides better autocompletion and helps prevent errors, especially when creating plugins.
Installation
The primary package you'll need for basic processing is @lottiefiles/relottie. This package bundles the core processor, the Lottie parser (@lottiefiles/relottie-parse), and the Lottie stringifier (@lottiefiles/relottie-stringify).
For creating plugins and interacting with the Lottie Abstract Syntax Tree (LAST), you'll also need @lottiefiles/last for type definitions and unist-util-visit for tree traversal. If you are working in a TypeScript project, you'll likely also need unified and vfile as development dependencies.
npm install @lottiefiles/relottie @lottiefiles/last unist-util-visit
# For TypeScript projects, also consider adding:
npm install unified vfile --save-dev
# or
yarn add @lottiefiles/relottie @lottiefiles/last unist-util-visit
# For TypeScript projects, also consider adding:
yarn add unified vfile --devBasic Processing Pipeline
relottie works by processing content through a pipeline managed by the underlying unified engine. The simplest pipeline involves:
Parsing: Converting input Lottie JSON into a LAST tree.
Stringifying: Converting the LAST tree back into output Lottie JSON.
The @lottiefiles/relottie package provides a convenient way to set up this basic pipeline.
Example: Processing with a Simple Plugin
Let's adapt the version update plugin from the Quick Start guide to demonstrate the processing pipeline with a transformation. This example will parse a Lottie JSON string, update its version using a custom plugin, and then stringify it back to a JSON string.
import { TITLES } from "@lottiefiles/last";
import type { Root, Attribute } from "@lottiefiles/last";
import { relottie } from "@lottiefiles/relottie";
import type { Plugin } from "unified";
import { CONTINUE, EXIT, visit } from "unist-util-visit";
import type { Data, VFile } from "vfile";
const { string: ST } = TITLES;
// Define potential options for our plugin
interface Options {
ignore: boolean;
version: string;
}
// Define the type for plugin options, making them all optional
export type PluginOptions = Partial<Options>;
// Define default options for the plugin
export const DEFAULT_OPTIONS: Options = {
ignore: false,
version: "6.0.0", // Default version to set
};
// Define a custom data structure to be attached to the VFile's `data` property.
// This allows plugins to pass information to each other or to the calling process.
export interface FileData extends Data {
updateVersionData?: {
// Optional property
oldVersion?: string; // Optional property
newVersion?: string; // Optional property
};
}
// Define our simple plugin to update the Lottie version
const updateVersionPlugin: Plugin<[PluginOptions?], Root> = (ops: PluginOptions = {}) => {
// Merge provided options with defaults
const options: Options = { ...DEFAULT_OPTIONS, ...ops };
// Prepare an object to store data related to this plugin's operation
const pluginOperationData: FileData["updateVersionData"] = {};
// The transformer function that unified will call with the AST (tree) and file (VFile)
function transformer(tree: Root, file: VFile): void {
if (options.ignore) {
console.log("Version update ignored due to plugin options.");
return;
}
visit(tree, "attribute", (node: Attribute, _parent) => {
if (node.title !== ST.version) return CONTINUE; // Skip if not the version attribute
const primitiveNode = node.children[0];
if (!primitiveNode) {
console.warn("Version attribute is missing a primitive node. Skipping update.");
return CONTINUE;
}
if (typeof primitiveNode.value !== "string") {
console.warn("Version attribute value is not a string. Skipping update.");
return CONTINUE;
}
// Store the old version and the new version
pluginOperationData.oldVersion = primitiveNode.value;
pluginOperationData.newVersion = options.version;
// Update the version value
primitiveNode.value = options.version;
console.log(`Version updated from ${pluginOperationData.oldVersion} to ${primitiveNode.value}`);
return EXIT; // Stop traversing, version found and updated
});
// Attach the collected data to the VFile for potential use by other plugins or the main process
// Ensure 'file.data' is initialized if it's not already
file.data = file.data || {};
file.data.updateVersionData = pluginOperationData;
}
return transformer;
};
const inputLottie = '{"v":"5.5.7","nm":"Example Animation","w":512,"h":512,"layers":[]}';
async function processLottieWithPlugin() {
try {
// Create a relottie processor, use our plugin with options, then process
const file = await relottie().use(updateVersionPlugin, { version: "7.0.0", ignore: false }).process(inputLottie);
const outputLottie = String(file); // Get the stringified Lottie JSON
console.log("\\nProcessed Lottie:");
console.log(outputLottie);
// Access data attached by the plugin via VFile.data
const processedFileData = file.data as FileData;
if (processedFileData.updateVersionData) {
console.log("\\nPlugin Operation Data:");
console.log(` - Old version: ${processedFileData.updateVersionData.oldVersion}`);
console.log(` - New version: ${processedFileData.updateVersionData.newVersion}`);
}
} catch (error) {
console.error("Error processing Lottie:", error);
}
}
processLottieWithPlugin();Running this script (e.g., with npx ts-node your-script.ts) would output something like:
Version updated from 5.5.7 to 7.0.0
Processed Lottie:
{"v":"7.0.0","nm":"Example Animation","w":512,"h":512,"layers":[]}
Plugin Operation Data:
- Old version: 5.5.7
- New version: 7.0.0Explanation of Key Parts:
Imports:
TITLESfrom@lottiefiles/last: Provides standardized semantic titles for Lottie properties, ensuring consistent access.Root,Attributefrom@lottiefiles/last: TypeScript types for the AST nodes.relottiefrom@lottiefiles/relottie: The main entry point to create a processor.Pluginfromunified: The base type for creating plugins.CONTINUE,EXIT,visitfromunist-util-visit: Utilities for traversing the AST.visitis used to find specific nodes,CONTINUEtells the visitor to keep searching, andEXITstops the traversal.Data,VFilefromvfile:VFileis a virtual file representation used byunifiedto carry the content and metadata through the pipeline.Datais an interface for theVFile.dataproperty.
Options,PluginOptions,DEFAULT_OPTIONS: These define the configuration that can be passed to ourupdateVersionPlugin.PluginOptions(usingPartial<Options>) makes all options optional, andDEFAULT_OPTIONSprovides sensible defaults.FileData: This interface extendsVFile'sDatatype to define a custom structure (updateVersionData) where our plugin can store information it gathered or produced. This data can be accessed by subsequent plugins or by the code that initiated the processing.updateVersionPlugin:It's a function that accepts
PluginOptionsand returns atransformerfunction.The
transformerfunction is the core of the plugin. It receives thetree(the AST, typed asRoot) and thefile(aVFileinstance).visit(tree, "attribute", ...): This traverses the AST, looking for nodes of type"attribute". The callback function is executed for eachAttributenode found.if (node.title !== ST.version) return CONTINUE;: This checks if the current attribute's semantictitleis the Lottie version (usingST.versionfromTITLES).primitiveNode.value = options.version;: Modifies the Lottie version in the AST.file.data.updateVersionData = pluginOperationData;: Attaches the collected data (old and new version) tofile.data.
processLottieWithPluginFunction:relottie(): Creates a new processor instance..use(updateVersionPlugin, { version: "7.0.0", ignore: false }): Registers our plugin and passes options to it..process(inputLottie): Starts the processing.
This example illustrates how relottie parses the input, allows a plugin to modify the AST, and then stringifies the modified AST back into a Lottie JSON string. The VFile object plays a crucial role in carrying both the content and any metadata or operational data through this pipeline.
Understanding the process Method
relottie()creates a new processor instance configured with the default Lottie parser and stringifier..process(input)takes the input Lottie JSON (as a string, Buffer, or VFile) and runs it through the pipeline. This is an asynchronous operation.Input: The
inputcan be:A Lottie JSON string.
A Node.js
Buffercontaining Lottie JSON data.A
VFileinstance. If you provide aVFile,relottiecan use itspathfor error messages and itsvalueas the content to process. Plugins can also read from and write tovfile.data.
It returns a
Promisethat resolves to aVFileobject representing the processed file. ThisVFileobject will contain:The processed Lottie content (accessible via
String(file)orfile.value).Any messages (warnings, errors) generated during processing (on
file.messages).Any data attached by plugins to
file.data.
String(file)orfile.toString()extracts the resulting Lottie JSON string from theVFile.
Adding Plugins (use)
The real power of relottie comes from adding plugins to the pipeline using the .use() method. Plugins are inserted between the parsing and stringifying steps, allowing you to inspect, analyze, or modify the LAST tree.
You can chain multiple .use() calls to apply several plugins in sequence. Each plugin will operate on the AST as modified by the preceding plugins.
import { relottie } from "@lottiefiles/relottie";
import type { Plugin, Transformer } from "unified";
import type { Root, Attribute } from "@lottiefiles/last";
import { visit } from "unist-util-visit";
import type { VFile } from "vfile";
// Assume updateVersionPlugin is defined as in the previous example
// Another simple plugin: counts the number of top-level layers
interface LayerCounterFileData extends VFile { // Extends VFile, or use 'Data' for file.data
data: {
layerCount?: number;
}
}
const countLayersPlugin: Plugin<[], Root> = () => {
const transformer: Transformer<Root, Root> = (tree: Root, file: LayerCounterFileData) => {
let count = 0;
visit(tree, "collection", (node) => {
if (node.key === "layers" && node.title === "composition-children") {
// Assuming the direct children of the 'layers' collection's ArrayNode are layer objects
const arrayNode = node.children[0];
if (arrayNode && arrayNode.type === "array") {
count = arrayNode.children.length;
}
return false; // Stop traversal for this branch
}
});
file.data = file.data || {}; // Ensure data object exists
file.data.layerCount = count;
console.log(`Layer Counter Plugin: Found ${count} layers.`);
};
return transformer;
};
const inputLottie = '{"v":"5.5.7", "nm":"Example With Layers", "w":100, "h":100, "layers":[{"ty":4},{"ty":1}]}\';
async function processWithMultiplePlugins() {
try {
const file = await relottie()
.use(updateVersionPlugin, { version: "8.0.0" }) // First plugin: update version
.use(countLayersPlugin) // Second plugin: count layers
.process(inputLottie);
console.log("\\nFinal Lottie JSON:");
console.log(String(file));
// Access data from both plugins
const processedFileData = file.data as FileData & LayerCounterFileData['data'];
if (processedFileData.updateVersionData) {
console.log("\\nVersion Plugin Data:");
console.log(` - Old version: ${processedFileData.updateVersionData.oldVersion}`);
console.log(` - New version: ${processedFileData.updateVersionData.newVersion}`);
}
if (typeof processedFileData.layerCount === 'number') {
console.log("\\nLayer Counter Plugin Data:");
console.log(` - Layer count: ${processedFileData.layerCount}`);
}
} catch (error) {
console.error("Error during multi-plugin processing:", error);
}
}
processWithMultiplePlugins();Expected Output (example):
Version updated from 5.5.7 to 8.0.0
Layer Counter Plugin: Found 2 layers.
Final Lottie JSON:
{"v":"8.0.0","nm":"Example With Layers","w":100,"h":100,"layers":[{"ty":4},{"ty":1}]}
Version Plugin Data:
- Old version: 5.5.7
- New version: 8.0.0
Layer Counter Plugin Data:
- Layer count: 2In this chained example:
updateVersionPluginruns first, modifying the version in the AST and attachingupdateVersionDatatofile.data.countLayersPluginruns next, operating on the AST already modified byupdateVersionPlugin. It counts the layers and attacheslayerCounttofile.data.The final
file.datacontains information from both plugins.
Next Steps
Now that you have a basic understanding of setting up relottie and its processing pipeline, you can explore more specific tasks:
Guide: Analyzing Lottie Files: Use plugins to extract information.
Guide: Modifying Lottie Files: Learn techniques for changing Lottie content.
Guide: Working with LAST: Understand the tree structure in more detail.
API Reference: Consult the detailed API documentation.