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
  • 1 copy
  • 11 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…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.