Claude
Skills
Sign in
Back

lottie

Included with Lifetime
$97 forever

Setup Lottie animations for Next.js with JSON-based vector animations. Use this skill when the user says "setup lottie", "add lottie", "lottie animation", "json animation", or "vector animation".

Web Dev

What this skill does


# Lottie Animations for Next.js

Lightweight, scalable JSON-based animations using lottie-react. Perfect for micro-interactions, loading states, icons, and hero animations.

## Why Lottie?

- **Tiny file sizes**: Up to 600% smaller than GIFs
- **Infinite scalability**: Vector-based, sharp at any resolution
- **Full control**: Play, pause, speed, direction, segments
- **Interactive**: Scroll-sync, hover triggers, click events
- **Cross-platform**: Same JSON works on web, iOS, Android

## Installation

```bash
bun add lottie-react
```

**Version Compatibility:**
- Next.js 15+/16+ with React 19: Works with dynamic imports (`ssr: false`)
- lottie-react wraps lottie-web, which requires browser APIs
- Note: Next.js 16 removed the `eslint` key from `next.config.ts` — use Biome instead

## Next.js App Router Setup

Lottie uses browser APIs (canvas/SVG). Use dynamic imports to prevent SSR errors.

### 1. Create the Lottie Component

```tsx
// components/lottie/lottie-animation.tsx
"use client";

import { useEffect, useRef } from "react";
import Lottie from "lottie-react";
import type { LottieRefCurrentProps } from "lottie-react";

type LottieAnimationProps = {
  animationData: object;
  loop?: boolean;
  autoplay?: boolean;
  speed?: number;
  direction?: 1 | -1;
  className?: string;
  onComplete?: () => void;
  onLoopComplete?: () => void;
};

export function LottieAnimation({
  animationData,
  loop = true,
  autoplay = true,
  speed = 1,
  direction = 1,
  className,
  onComplete,
  onLoopComplete,
}: LottieAnimationProps) {
  const lottieRef = useRef<LottieRefCurrentProps>(null);

  useEffect(() => {
    if (lottieRef.current) {
      lottieRef.current.setSpeed(speed);
      lottieRef.current.setDirection(direction);
    }
  }, [speed, direction]);

  return (
    <Lottie
      lottieRef={lottieRef}
      animationData={animationData}
      loop={loop}
      autoplay={autoplay}
      className={className}
      onComplete={onComplete}
      onLoopComplete={onLoopComplete}
    />
  );
}
```

### 2. Create Dynamic Import Wrapper

```tsx
// components/lottie/dynamic-lottie.tsx
"use client";

import dynamic from "next/dynamic";

export const DynamicLottie = dynamic(
  () => import("./lottie-animation").then((mod) => mod.LottieAnimation),
  {
    ssr: false,
    loading: () => (
      <div className="flex h-full w-full items-center justify-center">
        <div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-500" />
      </div>
    ),
  }
);
```

### 3. Store Animation JSON Files

Place Lottie JSON files in `src/animations/` so the `@/` alias works (since `@/` maps to `./src/`). Do NOT put them under `public/` if you plan to import them — the `@/public/` path will fail because `@/` resolves to `./src/`, not the project root.

```
src/
  animations/
    checkmark.json
    loading.json
    success.json
```

### 4. Use in Page

**Important:** If you pass event handler props (like `onComplete`, `onLoopComplete`), the parent must be a Client Component. Server Components cannot pass functions as props to Client Components.

```tsx
// app/page.tsx
"use client";

import { DynamicLottie } from "@/components/lottie/dynamic-lottie";
import checkmarkAnimation from "@/animations/checkmark.json";

export default function Page() {
  return (
    <div className="h-64 w-64">
      <DynamicLottie
        animationData={checkmarkAnimation}
        loop={false}
        onComplete={() => console.log("Animation complete!")}
      />
    </div>
  );
}
```

If you don't need event handlers, the page can remain a Server Component:

```tsx
// app/page.tsx (Server Component - no event handlers)
import { DynamicLottie } from "@/components/lottie/dynamic-lottie";
import checkmarkAnimation from "@/animations/checkmark.json";

export default function Page() {
  return (
    <div className="h-64 w-64">
      <DynamicLottie animationData={checkmarkAnimation} loop={false} />
    </div>
  );
}
```

## Core Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `animationData` | `object` | required | The Lottie JSON data |
| `loop` | `boolean \| number` | `true` | Loop infinitely or N times |
| `autoplay` | `boolean` | `true` | Start playing immediately |
| `speed` | `number` | `1` | Playback speed (1 = normal) |
| `initialSegment` | `[number, number]` | - | Start/end frames |
| `style` | `CSSProperties` | - | Container styles |
| `className` | `string` | - | Container class |

## Playback Control Methods

Access via `lottieRef`:

```tsx
"use client";

import Lottie from "lottie-react";
import type { LottieRefCurrentProps } from "lottie-react";
import { useRef } from "react";
import animationData from "./animation.json";

export function ControlledAnimation() {
  const lottieRef = useRef<LottieRefCurrentProps>(null);

  return (
    <div>
      <Lottie
        lottieRef={lottieRef}
        animationData={animationData}
        autoplay={false}
      />

      <div className="flex gap-2 mt-4">
        <button onClick={() => lottieRef.current?.play()}>
          Play
        </button>
        <button onClick={() => lottieRef.current?.pause()}>
          Pause
        </button>
        <button onClick={() => lottieRef.current?.stop()}>
          Stop
        </button>
        <button onClick={() => lottieRef.current?.setSpeed(2)}>
          2x Speed
        </button>
        <button onClick={() => lottieRef.current?.setDirection(-1)}>
          Reverse
        </button>
        <button onClick={() => lottieRef.current?.goToAndPlay(0, true)}>
          Restart
        </button>
      </div>
    </div>
  );
}
```

### All Methods

| Method | Parameters | Description |
|--------|------------|-------------|
| `play()` | - | Start playback |
| `pause()` | - | Pause at current frame |
| `stop()` | - | Stop and reset to frame 0 |
| `setSpeed(speed)` | `number` | Set playback speed |
| `setDirection(dir)` | `1 \| -1` | Forward or reverse |
| `goToAndPlay(value, isFrame?)` | `number, boolean` | Jump and play |
| `goToAndStop(value, isFrame?)` | `number, boolean` | Jump and stop |
| `playSegments(segments, force?)` | `[number, number][], boolean` | Play specific segments |
| `getDuration(inFrames?)` | `boolean` | Get total duration |
| `destroy()` | - | Cleanup |

## Event Callbacks

```tsx
<Lottie
  animationData={animationData}
  onComplete={() => console.log("Animation finished")}
  onLoopComplete={() => console.log("Loop completed")}
  onEnterFrame={(e) => console.log("Frame:", e.currentTime)}
  onSegmentStart={() => console.log("Segment started")}
  onConfigReady={() => console.log("Config ready")}
  onDataReady={() => console.log("Data loaded")}
  onDOMLoaded={() => console.log("DOM ready")}
  onDestroy={() => console.log("Destroyed")}
/>
```

## Hover Trigger Animation

```tsx
"use client";

import Lottie from "lottie-react";
import type { LottieRefCurrentProps } from "lottie-react";
import { useRef } from "react";
import iconAnimation from "./icon.json";

export function HoverIcon() {
  const lottieRef = useRef<LottieRefCurrentProps>(null);

  return (
    <div
      className="h-12 w-12 cursor-pointer"
      onMouseEnter={() => lottieRef.current?.play()}
      onMouseLeave={() => lottieRef.current?.stop()}
    >
      <Lottie
        lottieRef={lottieRef}
        animationData={iconAnimation}
        autoplay={false}
        loop={false}
      />
    </div>
  );
}
```

## Click Toggle Animation

```tsx
"use client";

import Lottie from "lottie-react";
import type { LottieRefCurrentProps } from "lottie-react";
import { useRef, useState } from "react";
import toggleAnimation from "./toggle.json";

export function ClickToggle() {
  const lottieRef = useRef<LottieRefCurrentProps>(null);
  const [isActive, setIsActive] = useState(false);

  const handleClick = () => {
    if (isActive) {
      lottieRef.current?.setDirection(-1);
    } else {
      lottieRef.current?.setDirection(1);
    }
    lottieRef.current?.play();
    setIsA
Files: 1
Size: 19.2 KB
Complexity: 28/100
Category: Web Dev

Related in Web Dev