Command Palette

Search for a command to run...

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: npm or yarn for 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 --dev

Basic Processing Pipeline

relottie works by processing content through a pipeline managed by the underlying unified engine. The simplest pipeline involves:

  1. Parsing: Converting input Lottie JSON into a LAST tree.

  2. 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.0

Explanation of Key Parts:

  • Imports:

    • TITLES from @lottiefiles/last: Provides standardized semantic titles for Lottie properties, ensuring consistent access.

    • Root, Attribute from @lottiefiles/last: TypeScript types for the AST nodes.

    • relottie from @lottiefiles/relottie: The main entry point to create a processor.

    • Plugin from unified: The base type for creating plugins.

    • CONTINUE, EXIT, visit from unist-util-visit: Utilities for traversing the AST. visit is used to find specific nodes, CONTINUE tells the visitor to keep searching, and EXIT stops the traversal.

    • Data, VFile from vfile: VFile is a virtual file representation used by unified to carry the content and metadata through the pipeline. Data is an interface for the VFile.data property.

  • Options, PluginOptions, DEFAULT_OPTIONS: These define the configuration that can be passed to our updateVersionPlugin. PluginOptions (using Partial<Options>) makes all options optional, and DEFAULT_OPTIONS provides sensible defaults.

  • FileData: This interface extends VFile's Data type 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 PluginOptions and returns a transformer function.

    • The transformer function is the core of the plugin. It receives the tree (the AST, typed as Root) and the file (a VFile instance).

    • visit(tree, "attribute", ...): This traverses the AST, looking for nodes of type "attribute". The callback function is executed for each Attribute node found.

    • if (node.title !== ST.version) return CONTINUE;: This checks if the current attribute's semantic title is the Lottie version (using ST.version from TITLES).

    • primitiveNode.value = options.version;: Modifies the Lottie version in the AST.

    • file.data.updateVersionData = pluginOperationData;: Attaches the collected data (old and new version) to file.data.

  • processLottieWithPlugin Function:

    • 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 input can be:

      • A Lottie JSON string.

      • A Node.js Buffer containing Lottie JSON data.

      • A VFile instance. If you provide a VFile, relottie can use its path for error messages and its value as the content to process. Plugins can also read from and write to vfile.data.

  • It returns a Promise that resolves to a VFile object representing the processed file. This VFile object will contain:

    • The processed Lottie content (accessible via String(file) or file.value).

    • Any messages (warnings, errors) generated during processing (on file.messages).

    • Any data attached by plugins to file.data.

  • String(file) or file.toString() extracts the resulting Lottie JSON string from the VFile.

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: 2

In this chained example:

  1. updateVersionPlugin runs first, modifying the version in the AST and attaching updateVersionData to file.data.

  2. countLayersPlugin runs next, operating on the AST already modified by updateVersionPlugin. It counts the layers and attaches layerCount to file.data.

  3. The final file.data contains 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:

Last updated: April 10, 2026 at 9:12 AMEdit this page