Skip to content
Back to skills

Lottie

ASecurity

Lottie plays vector animations exported from After Effects and other motion tools as JSON, rendered in the browser by the lottie-web player. Use when tasks involve adding motion graphics, animated icons, loading indicators, or micro-interactions to a web app: loading JSON or .lottie animation files, controlling playback, listening to events, recoloring at runtime, or integrating animations into React, Vue, or vanilla JS.

  • 142 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 29, 2026
designtypescriptrustgobashreactvuenodeexpressawsgit

Works with

  • terminal
  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add TerminalSkills/skills --skill lottie --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lottie?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Lottie
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/terminalskills-lottie/badge)](https://www.skillsdirectory.com/skills/terminalskills-lottie)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: lottie
description: >-
  Lottie plays vector animations exported from After Effects and other motion
  tools as JSON, rendered in the browser by the lottie-web player. Use when
  tasks involve adding motion graphics, animated icons, loading indicators, or
  micro-interactions to a web app: loading JSON or .lottie animation files,
  controlling playback, listening to events, recoloring at runtime, or
  integrating animations into React, Vue, or vanilla JS.
license: Apache-2.0
compatibility: "Browsers (lottie-web 5.x); the React example needs React 18+"
metadata:
  author: terminal-skills
  version: "1.1.0"
  category: design
  repository: https://github.com/airbnb/lottie-web
  tags: ["lottie", "animation", "after-effects", "motion", "micro-interactions"]
---

# Lottie

## Overview

Render After Effects animations exported as JSON. Lightweight, scalable, and interactive. `lottie-web` (Airbnb, current release 5.13.0) parses the JSON produced by the Bodymovin exporter and draws it as SVG, canvas or HTML, with an API for playback control and events. Compressed `.lottie` archives need the separate dotLottie player covered at the end of the instructions.

## Instructions

### Setup

```bash
# Install lottie-web for vanilla JS/TS projects.
npm install lottie-web
```

The default import bundles all three renderers plus the expression engine (about 77 KB gzipped). When an animation uses no After Effects expressions, import the light SVG-only build instead (about 47 KB gzipped, no `eval`):

```typescript
import lottie from "lottie-web/build/player/lottie_light";
```

### Basic Playback

```typescript
// src/lottie/player.ts — Load and play a Lottie animation in a DOM container.
// The animation JSON is typically exported from After Effects via Bodymovin.
import lottie, { AnimationItem } from "lottie-web";

export function playAnimation(
  container: HTMLElement,
  animationData: object
): AnimationItem {
  return lottie.loadAnimation({
    container,
    renderer: "svg", // "canvas" or "html" also available
    loop: true,
    autoplay: true,
    animationData,
  });
}

// Load from URL instead of inline data
export function playFromUrl(container: HTMLElement, path: string): AnimationItem {
  return lottie.loadAnimation({
    container,
    renderer: "svg",
    loop: true,
    autoplay: true,
    path, // URL to the JSON file
  });
}
```

`animationData` and `path` are mutually exclusive. The rendered SVG is sized to 100% of the container, so set the container's dimensions in CSS.

### Playback Controls

```typescript
// src/lottie/controls.ts — Control animation playback: play, pause, seek, speed.
import type { AnimationItem } from "lottie-web";

export function setupControls(anim: AnimationItem) {
  // Play / Pause
  anim.play();
  anim.pause();
  anim.stop();

  // Go to specific frame (frame 30, and play)
  anim.goToAndPlay(30, true);

  // Go to specific frame and stop
  anim.goToAndStop(0, true);

  // Playback speed (2x)
  anim.setSpeed(2);

  // Play direction (-1 = reverse)
  anim.setDirection(-1);

  // Play only a segment (frames 10-50)
  anim.playSegments([10, 50], true);
}
```

The second argument of `goToAndPlay` / `goToAndStop` selects frames; when it is `false` or omitted the value is a time in milliseconds. `anim.getDuration()` returns seconds, `anim.getDuration(true)` frames.

### Event Handling

```typescript
// src/lottie/events.ts — Listen to animation lifecycle events for triggering
// UI updates, chaining animations, or tracking analytics.
import type { AnimationItem } from "lottie-web";

export function attachEvents(anim: AnimationItem) {
  anim.addEventListener("complete", () => {
    console.log("Animation completed"); // not fired while loop is true
  });

  anim.addEventListener("loopComplete", () => {
    console.log("Loop finished");
  });

  anim.addEventListener("enterFrame", (e) => {
    // Fires every frame — use sparingly
    const progress = e.currentTime / e.totalTime;
    document.getElementById("progress")!.style.width = `${progress * 100}%`;
  });

  anim.addEventListener("DOMLoaded", () => {
    console.log("Animation DOM elements ready");
  });

  anim.addEventListener("data_failed", () => {
    console.error("Animation JSON could not be loaded from `path`");
  });
}
```

`addEventListener` returns a function that removes the listener.

### React Integration

```tsx
// src/components/LottiePlayer.tsx — React component wrapping lottie-web.
// Destroys the animation on unmount and reloads it when a prop changes.
import { useEffect, useRef } from "react";
import lottie, { AnimationItem } from "lottie-web";

interface Props {
  animationData: object;
  loop?: boolean;
  autoplay?: boolean;
  className?: string;
}

export function LottiePlayer({ animationData, loop = true, autoplay = true, className }: Props) {
  const containerRef = useRef<HTMLDivElement>(null);
  const animRef = useRef<AnimationItem | null>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    animRef.current = lottie.loadAnimation({
      container: containerRef.current,
      renderer: "svg",
      loop,
      autoplay,
      animationData,
    });

    return () => {
      animRef.current?.destroy();
    };
  }, [animationData, loop, autoplay]);

  return <div ref={containerRef} className={className} />;
}
```

### Dynamic Color Updates

```typescript
// src/lottie/theme.ts — Swap solid fill and stroke colors in a Lottie JSON before rendering.
// Useful for theming animations to match brand colors at runtime.
type RGB = [number, number, number]; // 0–1 floats, as stored in the file

export function recolorAnimation(animationData: any, colorMap: Record<string, RGB>): any {
  const data = structuredClone(animationData);

  const walkShapes = (shapes: any[]) => {
    for (const shape of shapes) {
      const isPaint = shape.ty === "fl" || shape.ty === "st"; // fill or stroke
      if (isPaint && shape.c?.a !== 1 && Array.isArray(shape.c?.k)) { // a === 1 means keyframed
        const next = colorMap[rgbToHex(shape.c.k[0], shape.c.k[1], shape.c.k[2])];
        if (next) shape.c.k = [...next, 1];
      }
      if (shape.it) walkShapes(shape.it); // groups nest their items in `it`
    }
  };
  const walkLayers = (layers: any[] = []) => {
    for (const layer of layers) if (layer.shapes) walkShapes(layer.shapes);
  };

  walkLayers(data.layers);
  for (const asset of data.assets ?? []) walkLayers(asset.layers); // precompositions
  return data;
}

function rgbToHex(r: number, g: number, b: number): string {
  return "#" + [r, g, b].map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("");
}
```

### dotLottie Files

A `.lottie` file is a ZIP archive holding one or more animations and their assets. lottie-web cannot read it; use the LottieFiles player (`npm install @lottiefiles/dotlottie-web`, or `@lottiefiles/dotlottie-react` for React), which draws on a canvas through a WebAssembly renderer and also accepts plain JSON:

```typescript
// src/lottie/dotlottie.ts — Play a .lottie file on a <canvas>.
import { DotLottie } from "@lottiefiles/dotlottie-web";

export const hero = new DotLottie({
  canvas: document.querySelector<HTMLCanvasElement>("#hero-animation")!,
  src: "/animations/hero.lottie",
  autoplay: true,
  loop: true,
});

hero.addEventListener("loadError", () => console.error("hero.lottie failed to load"));
// hero.pause(); hero.setSpeed(1.5); hero.setFrame(24); hero.destroy();
```

## Examples

### Example 1: Loading indicator while a form submits

User: "Show our loader animation while the order is being submitted."

```html
<div id="order-loader" style="width:96px;height:96px" role="img" aria-label="Submitting order"></div>
```

```typescript
import lottie from "lottie-web/build/player/lottie_light";

const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const loader = lottie.loadAnimation({
  container: document.getElementById("order-loader")!,
  renderer: "svg",
  loop: true,
  autoplay: !reduceMotion,
  path: "/animations/order-loader.json",
});

export async function submitOrder(form: HTMLFormElement) {
  try {
    await fetch("/api/orders", { method: "POST", body: new FormData(form) });
  } finally {
    loader.destroy(); // removes the SVG and stops the render loop
  }
}
```

A 96 px SVG loops inside the div until the request settles. With reduced motion enabled the first frame is drawn as a still image and nothing moves.

### Example 2: Play a success checkmark once, in the brand color

User: "After payment, play the checkmark animation one time and make it our green instead of blue."

```tsx
import checkmark from "./animations/checkmark.json";
import { LottiePlayer } from "./components/LottiePlayer";
import { recolorAnimation } from "./lottie/theme";

// Recolor once at module scope: a new object on every render would restart the animation
const brandCheck = recolorAnimation(checkmark, { "#3399ff": [0.13, 0.77, 0.37] });

export function PaymentSuccess() {
  return <LottiePlayer animationData={brandCheck} loop={false} className="h-24 w-24" />;
}
```

Every solid `#3399ff` fill and stroke renders as `rgb(33,196,94)`, the animation plays through once, fires `complete`, and rests on its last frame.

## Guidelines

- **Always destroy.** Call `anim.destroy()` when the element leaves the page (React cleanup, route change); otherwise the animation keeps its DOM nodes and frame loop alive.
- **Keep `animationData` stable in React.** The effect above reloads whenever the object identity changes, so import the JSON or memoize it. If an animation with repeaters is loaded several times from the same object, pass a deep clone to each `loadAnimation` call.
- **Pick the renderer deliberately.** `svg` is the default and stays sharp at any size; `canvas` draws into one element instead of creating a DOM node per shape, which helps with very complex animations; `html` is rarely needed.
- **Respect reduced motion** with `prefers-reduced-motion`, and label the container (`role="img"`, `aria-label`) or set `rendererSettings: { title, description }` for the SVG renderer.
- **Expressions run through `eval`.** The full build evaluates After Effects expressions stored in the JSON, which requires `unsafe-eval` under a strict Content-Security-Policy and means animation files from untrusted sources can run code. Use the light build for third-party files.
- **Tests in jsdom fail on import** with "Cannot set properties of null (setting 'fillStyle')" because jsdom has no canvas. Stub `HTMLCanvasElement.prototype.getContext` in the test setup or mock the `lottie-web` module.
- **Server rendering.** Importing lottie-web on the server is safe since 5.13.0, but `loadAnimation` needs a DOM: call it in `useEffect`, `onMounted` or a client-only component.
- **Export limits.** Image sequences, video and audio layers are not supported; large masks and huge shapes hurt frame rate. Raster images are exported to an `images/` folder next to the JSON; when loading through `animationData`, set `assetsPath` to that folder's URL. Serve the JSON gzipped.
- **dotLottie loads its WASM from a CDN** (jsDelivr) at runtime by default. For offline apps or a strict CSP, host the file yourself and call `DotLottie.setWasmUrl()` before creating a player.
- **React wrappers.** `lottie-react` (v3: `import { Lottie } from "lottie-react"`, `<Lottie src="/hero.json" autoplay loop />`) and `@lottiefiles/dotlottie-react` (`<DotLottieReact src="/hero.lottie" autoplay loop />`) save writing the component above.
- **When not to use Lottie.** Simple hovers and transitions are cheaper in CSS; long video-like sequences are smaller as video.

Files in this skill

  • SKILL.md4.9 KB
  • _scores.json1.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…