DEV Community

Fazal Shah
Fazal Shah

Posted on

Using Lottie Animations with TypeScript: A Typed Guide

TypeScript adds type safety to Lottie integrations — typed animation data, typed refs, typed event callbacks, and typed programmatic APIs. This guide covers every TypeScript-specific pattern for lottie-web and the React/Vue wrappers.


Before You Start

Open your Lottie files in IconKing first:

  • Verify colors, timing, and layer structure
  • Convert .json.lottie for 75% smaller files
  • Catch rendering issues before wiring up TypeScript

Setup: lottie-web with TypeScript

npm install lottie-web
npm install --save-dev @types/lottie-web
Enter fullscreen mode Exit fullscreen mode

The @types/lottie-web package provides full typings for the lottie-web API.


Basic Typed Usage

import lottie, { AnimationItem, AnimationConfig } from 'lottie-web';

const config: AnimationConfig = {
  container: document.getElementById('container') as HTMLElement,
  renderer: 'svg',
  loop: true,
  autoplay: true,
  path: '/animations/loading.json',
};

const anim: AnimationItem = lottie.loadAnimation(config);
Enter fullscreen mode Exit fullscreen mode

The AnimationItem type gives you full autocomplete on methods:

anim.play();
anim.pause();
anim.stop();
anim.destroy();
anim.setSpeed(1.5);
anim.setDirection(1);    // 1 = forward, -1 = reverse
anim.goToAndPlay(0, true); // frame 0, isFrame = true
anim.goToAndStop(30, true);
Enter fullscreen mode Exit fullscreen mode

Typed Animation Data

When bundling animation JSON directly (not via path), type the import:

import type { AnimationData } from 'lottie-web';
import animJson from './animations/loading.json';

// Cast the imported JSON to AnimationData
const animData = animJson as unknown as AnimationData;

const anim = lottie.loadAnimation({
  container: el,
  renderer: 'svg',
  loop: true,
  autoplay: true,
  animationData: animData,
});
Enter fullscreen mode Exit fullscreen mode

The as unknown as AnimationData cast is necessary because TypeScript infers imported JSON as a literal type, not the broader AnimationData shape.


Typed Event Listeners

import lottie, { AnimationItem, AnimationEventName } from 'lottie-web';

const anim: AnimationItem = lottie.loadAnimation({ /* ... */ });

// Typed event names
anim.addEventListener('complete', () => {
  console.log('Animation finished');
});

anim.addEventListener('loopComplete', () => {
  console.log('Loop completed');
});

anim.addEventListener('enterFrame', (e) => {
  // e.currentTime is the current frame number
  console.log('Frame:', e.currentTime);
});

anim.addEventListener('segmentStart', (e) => {
  console.log('Segment started:', e.firstFrame, e.totalFrames);
});

anim.addEventListener('DOMLoaded', () => {
  console.log('DOM ready, can use addValueCallback');
});
Enter fullscreen mode Exit fullscreen mode

Available typed event names:

  • 'complete' — non-looping animation finished
  • 'loopComplete' — one loop cycle finished
  • 'enterFrame' — fires on each frame
  • 'segmentStart' — new segment started
  • 'DOMLoaded' — animation SVG/canvas rendered
  • 'destroy' — animation instance destroyed
  • 'data_ready' — animation data loaded
  • 'data_failed' — animation data failed to load

Typed addValueCallback

import lottie, { AnimationItem } from 'lottie-web';

type LottieColor = [number, number, number, number]; // [R, G, B, A] 0–1

const anim: AnimationItem = lottie.loadAnimation({ /* ... */ });

function hexToLottieColor(hex: string): LottieColor {
  const r = parseInt(hex.slice(1, 3), 16) / 255;
  const g = parseInt(hex.slice(3, 5), 16) / 255;
  const b = parseInt(hex.slice(5, 7), 16) / 255;
  return [r, g, b, 1];
}

anim.addEventListener('DOMLoaded', () => {
  anim.addValueCallback(
    ['**', 'Fill 1', 'Color'],
    (): LottieColor => hexToLottieColor('#3366FF')
  );
});
Enter fullscreen mode Exit fullscreen mode

React + TypeScript

With lottie-react

npm install lottie-react
Enter fullscreen mode Exit fullscreen mode

lottie-react ships with its own TypeScript types:

import Lottie, { LottieRefCurrentProps } from 'lottie-react';
import { useRef } from 'react';
import animData from './animations/success.json';

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

  function handlePlay() {
    lottieRef.current?.play();
  }

  function handleStop() {
    lottieRef.current?.stop();
  }

  return (
    <>
      <div style={{ width: 200, height: 200 }}>
        <Lottie
          lottieRef={lottieRef}
          animationData={animData}
          loop={false}
          autoplay={false}
          onComplete={() => console.log('done')}
        />
      </div>
      <button onClick={handlePlay}>Play</button>
      <button onClick={handleStop}>Stop</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Typed Props Interface

interface AnimatedIconProps {
  animationData: object;
  size?: number;
  loop?: boolean;
  autoplay?: boolean;
  onComplete?: () => void;
  className?: string;
}

export function AnimatedIcon({
  animationData,
  size = 48,
  loop = false,
  autoplay = true,
  onComplete,
  className,
}: AnimatedIconProps) {
  return (
    <div style={{ width: size, height: size }} className={className}>
      <Lottie
        animationData={animationData}
        loop={loop}
        autoplay={autoplay}
        onComplete={onComplete}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Custom Hook with Types

import { useRef, useEffect, RefObject } from 'react';
import lottie, { AnimationItem } from 'lottie-web';

interface UseLottieOptions {
  path: string;
  loop?: boolean;
  autoplay?: boolean;
}

interface UseLottieReturn {
  containerRef: RefObject<HTMLDivElement>;
  play: () => void;
  pause: () => void;
  stop: () => void;
  setSpeed: (speed: number) => void;
}

export function useLottie({
  path,
  loop = true,
  autoplay = true,
}: UseLottieOptions): UseLottieReturn {
  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,
      path,
    });

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

  return {
    containerRef,
    play: () => animRef.current?.play(),
    pause: () => animRef.current?.pause(),
    stop: () => animRef.current?.stop(),
    setSpeed: (speed) => animRef.current?.setSpeed(speed),
  };
}

// Usage
function MyComponent() {
  const { containerRef, play, pause } = useLottie({
    path: '/animations/icon.json',
    loop: true,
    autoplay: true,
  });

  return (
    <div>
      <div ref={containerRef} style={{ width: 100, height: 100 }} />
      <button onClick={play}>Play</button>
      <button onClick={pause}>Pause</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Vue 3 + TypeScript

npm install vue3-lottie
Enter fullscreen mode Exit fullscreen mode
<script setup lang="ts">
import { ref } from 'vue'
import Vue3Lottie, { LottieAnimation } from 'vue3-lottie'
import animData from './animations/loading.json'

const lottieRef = ref<InstanceType<typeof LottieAnimation> | null>(null)

function play() {
  lottieRef.value?.play()
}

function pause() {
  lottieRef.value?.pause()
}
</script>

<template>
  <Vue3Lottie
    ref="lottieRef"
    :animation-data="animData"
    :loop="true"
    :auto-play="true"
    :width="200"
    :height="200"
  />
  <button @click="play">Play</button>
  <button @click="pause">Pause</button>
</template>
Enter fullscreen mode Exit fullscreen mode

dotLottie + TypeScript

@lottiefiles/dotlottie-web ships with TypeScript types:

npm install @lottiefiles/dotlottie-web
Enter fullscreen mode Exit fullscreen mode
import { DotLottie, DotLottieConfig } from '@lottiefiles/dotlottie-web';

const config: DotLottieConfig = {
  canvas: document.getElementById('canvas') as HTMLCanvasElement,
  src: '/animations/loading.lottie',
  loop: true,
  autoplay: true,
};

const dotLottie: DotLottie = new DotLottie(config);

// Typed event listeners
dotLottie.addEventListener('play', () => console.log('playing'));
dotLottie.addEventListener('complete', () => console.log('done'));
dotLottie.addEventListener('frame', (e) => {
  console.log('Frame:', e.currentFrame);
});

// Cleanup
dotLottie.destroy();
Enter fullscreen mode Exit fullscreen mode

React + dotLottie TypeScript

npm install @lottiefiles/dotlottie-react
Enter fullscreen mode Exit fullscreen mode
import { DotLottieReact } from '@lottiefiles/dotlottie-react';
import type { DotLottieReactProps } from '@lottiefiles/dotlottie-react';

interface AnimatedLoaderProps extends Partial<DotLottieReactProps> {
  size?: number;
}

export function AnimatedLoader({ size = 100, ...props }: AnimatedLoaderProps) {
  return (
    <DotLottieReact
      src="/animations/loading.lottie"
      loop
      autoplay
      style={{ width: size, height: size }}
      {...props}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Typing Animation Segments

type AnimationSegment = [number, number]; // [startFrame, endFrame]

const segments: Record<string, AnimationSegment> = {
  idle: [0, 30],
  active: [31, 60],
  success: [61, 90],
};

function playSegment(anim: AnimationItem, name: keyof typeof segments) {
  anim.playSegments(segments[name], true);
}

// Usage
playSegment(anim, 'success');
Enter fullscreen mode Exit fullscreen mode

tsconfig Notes

If you're importing .json directly, enable these in tsconfig.json:

{
  "compilerOptions": {
    "resolveJsonModule": true,
    "esModuleInterop": true
  }
}
Enter fullscreen mode Exit fullscreen mode

Summary

  • Install @types/lottie-web for full lottie-web typings
  • Use AnimationItem type for animation instances
  • Type refs as useRef<LottieRefCurrentProps>(null) with lottie-react
  • Define LottieColor as [number, number, number, number] for addValueCallback
  • @lottiefiles/dotlottie-web and @lottiefiles/dotlottie-react include types out of the box
  • Use keyof typeof patterns for typed segment names

Before integrating: preview and convert files at IconKing — no account needed.

Top comments (0)